Merge commit 'bd31f9c6f859b711c3d838b9341ff39ae9351280' into iipbs/sn/dev
This commit is contained in:
+5
-5
@@ -1,12 +1,13 @@
|
||||
NEXT_PUBLIC_SITE_ID = DM
|
||||
NEXT_PUBLIC_SITE_ID = DN
|
||||
NEXT_PUBLIC_PORT = 3015
|
||||
NEXT_PUBLIC_CLAIM_NAME = Authorization
|
||||
NEXT_PUBLIC_AUTH_USER_ID = token-id
|
||||
NEXT_PUBLIC_ACCESS_TOKEN = access-token
|
||||
NEXT_PUBLIC_REFRESH_TOKEN = refresh-token
|
||||
NEXT_PUBLIC_PORT = 3005
|
||||
NEXT_PUBLIC_PROXY_HOST = http://localhost:3005
|
||||
NEXT_PUBLIC_CS_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
NEXT_PUBLIC_PROXY_HOST = http://localhost:3015
|
||||
NEXT_PUBLIC_CM_LOGIN_PAGE_URL = http://dev.onasys.co.kr:3000/auth/login
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
NEXT_PUBLIC_CS_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
NEXT_PUBLIC_DM_SERVER_API_URL = http://con.mind-one.co.kr:11083/reuse-service/api/v1
|
||||
# NEXT_PUBLIC_FO_SERVER_API_URL = http://192.168.1.222:8883/operation-service/api/v1
|
||||
NEXT_PUBLIC_FO_SERVER_API_URL = http://con.mind-one.co.kr:11083/operation-service/api/v1
|
||||
@@ -19,7 +20,6 @@ NEXT_PUBLIC_NGII_PARAM = 04trYP9_xwLAfALjwZ-B8g&service=WMTS&request=GetTile&ver
|
||||
NEXT_PUBLIC_GATEWAY_URL=http://con.mind-one.co.kr:11083 # 개발 서버 API
|
||||
#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.128:8883/sewerage-service/api # 최용성
|
||||
#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.66:8883/sewerage-service/api # 박이정
|
||||
NEXT_PUBLIC_CM_LOGIN_PAGE_URL = /
|
||||
|
||||
|
||||
NEXT_PUBLIC_FINDADRESS_URL=https://business.juso.go.kr/addrlink/addrLinkApi.do
|
||||
|
||||
+11
-9
@@ -1,23 +1,25 @@
|
||||
NEXT_PUBLIC_SITE_ID = DM
|
||||
NEXT_PUBLIC_PORT = 11035
|
||||
NEXT_PUBLIC_PROXY_HOST = http://con.mind-one.co.kr:11035
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
NEXT_PUBLIC_DM_SERVER_API_URL = http://con.mind-one.co.kr:11083/reuse-service/api/v1
|
||||
NEXT_PUBLIC_FO_SERVER_API_URL = http://con.mind-one.co.kr:11083/operation-service/api/v1
|
||||
NEXT_PUBLIC_CS_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
NEXT_PUBLIC_CS_TUS_UPLOAD = /attach-service/api/v1/tus/upload
|
||||
NEXT_PUBLIC_SITE_ID = DN
|
||||
NEXT_PUBLIC_PORT = 11315
|
||||
NEXT_PUBLIC_CLAIM_NAME = Authorization
|
||||
NEXT_PUBLIC_AUTH_USER_ID = token-id
|
||||
NEXT_PUBLIC_ACCESS_TOKEN = access-token
|
||||
NEXT_PUBLIC_REFRESH_TOKEN = refresh-token
|
||||
NEXT_PUBLIC_PROXY_HOST = http://con.mind-one.co.kr:11315
|
||||
NEXT_PUBLIC_CM_LOGIN_PAGE_URL = http://dev.onasys.co.kr:3000/auth/login
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
NEXT_PUBLIC_CS_SERVER_API_URL = http://dev.onasys.co.kr:8000
|
||||
NEXT_PUBLIC_DM_SERVER_API_URL = http://con.mind-one.co.kr:11083/reuse-service/api/v1
|
||||
NEXT_PUBLIC_FO_SERVER_API_URL = http://con.mind-one.co.kr:11083/operation-service/api/v1
|
||||
NEXT_PUBLIC_WR_SERVER_API_URL = http://con.mind-one.co.kr:11083/reuse-service/api/v1
|
||||
#NEXT_PUBLIC_GEOSERVER_URL = http://rsone.iptime.org:10010/geoserver
|
||||
#NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver
|
||||
NEXT_PUBLIC_CS_TUS_UPLOAD = /attach-service/api/v1/tus/upload
|
||||
NEXT_PUBLIC_NGII_URL = https://map.ngii.go.kr
|
||||
NEXT_PUBLIC_NGII_PARAM = 04trYP9_xwLAfALjwZ-B8g&service=WMTS&request=GetTile&version=1.0.0&format=image/png&layer=korean_map&style=korean&tilematrixset=korean&tilematrix={z}&tilecol={x}&tilerow={y}
|
||||
NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service # 개발 서버 API
|
||||
NEXT_PUBLIC_GATEWAY_URL=http://con.mind-one.co.kr:11083 # 개발 서버 API
|
||||
# NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.128:8883/sewerage-service/api # 최용성
|
||||
#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.66:8883/sewerage-service/api # 박이정
|
||||
NEXT_PUBLIC_CM_LOGIN_PAGE_URL = /
|
||||
|
||||
|
||||
NEXT_PUBLIC_FINDADRESS_URL=https://business.juso.go.kr/addrlink/addrLinkApi.do
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# 공단 운영
|
||||
NEXT_PUBLIC_SITE_ID = DM
|
||||
NEXT_PUBLIC_SITE_ID = DN
|
||||
NEXT_PUBLIC_PORT = 3000
|
||||
NEXT_PUBLIC_CLAIM_NAME = Authorization
|
||||
NEXT_PUBLIC_AUTH_USER_ID = token-id
|
||||
@@ -8,16 +8,16 @@ NEXT_PUBLIC_REFRESH_TOKEN = refresh-token
|
||||
#NEXT_PUBLIC_PROXY_HOST = https://devreuse.hasuon.or.kr
|
||||
#NEXT_PUBLIC_CM_LOGIN_PAGE_URL = https://devqueen.hasuon.or.kr/auth/login
|
||||
NEXT_PUBLIC_SERVER_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000
|
||||
NEXT_PUBLIC_FO_SERVER_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/operation-service/api/v1
|
||||
NEXT_PUBLIC_CS_SERVER_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000
|
||||
NEXT_PUBLIC_CS_TUS_UPLOAD = /attach-service/api/v1/tus/upload
|
||||
NEXT_PUBLIC_FO_SERVER_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/operation-service/api/v1
|
||||
NEXT_PUBLIC_WR_SERVER_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/reuse-service/api/v1
|
||||
NEXT_PUBLIC_GEOSERVER_URL = http://172.29.143.87:8080/geoserver
|
||||
NEXT_PUBLIC_LX_GEOSERVER_URL = http://172.29.143.87:8080/geoserver_swl
|
||||
NEXT_PUBLIC_WR_SERVER_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/reuse-service/api/v1
|
||||
NEXT_PUBLIC_CS_TUS_UPLOAD = /attach-service/api/v1/tus/upload
|
||||
NEXT_PUBLIC_SEWERAGE_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/sewerage-service
|
||||
NEXT_PUBLIC_GATEWAY_URL = http://apigateway.iipbs-was.svc.cluster.local:8000
|
||||
NEXT_PUBLIC_NGII_URL = http://172.29.143.87/poiact
|
||||
NEXT_PUBLIC_NGII_PARAM = BDE2BEF39DAFEC293AB6730150F222AC&service=WMTS&request=GetTile&version=1.0.0&format=image/png&layer=korean_map&style=korean&tilematrixset=korean&tilematrix={z}&tilecol={y}&tilerow={x}
|
||||
NEXT_PUBLIC_GATEWAY_URL = http://apigateway.iipbs-was.svc.cluster.local:8000
|
||||
|
||||
|
||||
NEXT_PUBLIC_FINDADRESS_URL=https://business.juso.go.kr/addrlink/addrLinkApi.do
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/.pnp
|
||||
.pnp.*
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/versions
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# env files (can opt-in for committing if needed)
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
@@ -1,16 +0,0 @@
|
||||
"use client";
|
||||
import Base from "../base";
|
||||
import "../globals.css";
|
||||
import { StoreProvider } from "../store";
|
||||
|
||||
export default function BaseLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<StoreProvider>
|
||||
<Base>{children}</Base>
|
||||
</StoreProvider>
|
||||
);
|
||||
}
|
||||
@@ -1,209 +0,0 @@
|
||||
"use client"
|
||||
import { Table, TableBody, TableCell, TableRow } from '@common/components/ui/table';
|
||||
|
||||
import { useDmStore } from '@/app/store';
|
||||
import { ContentsDiv, Title, UiModal, UiTable } from '@common/business_components/ui';
|
||||
import ListDiv from '@common/business_components/ui/list_div';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Toaster } from 'react-hot-toast';
|
||||
import SdmLogin from '../sdmLogin/page';
|
||||
import type { DemandCompBizType } from '@/app/store/sdmMng';
|
||||
import TransValueFormat from '@/app/common/transValueFormat';
|
||||
import { useRouter } from 'next/navigation';
|
||||
|
||||
const TABLE_STYLES = {
|
||||
header: "text-center border border-[var(--border)] bg-[rgb(234,243,251)] text-[rgb(100,119,132)] whitespace-pre-line",
|
||||
cell: "text-right whitespace-pre-line",
|
||||
fullWidth: "text-center border border-[var(--border)] bg-[rgb(234,243,251)] text-[rgb(100,119,132)] whitespace-pre-line",
|
||||
table: "fixed w-full"
|
||||
} as const;
|
||||
|
||||
type SdmApplyProps = {
|
||||
sdmNo : string;
|
||||
}
|
||||
|
||||
export default function SdmApplyList({}: SdmApplyProps) {
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const {getSdmInfoStatusList, Sdm, Common} = useDmStore((state) => state);
|
||||
const {successLogin, sdmInfoStatusList, upHeadSdmInfoStatus} = Sdm;
|
||||
const {selectSidoData, selectSigunData} = Common;
|
||||
const [isLogin, setIsLogin] = useState(false);
|
||||
const [isLoginStorageInfo, setIsLoginStorageInfo] = useState(false);
|
||||
const [sdmApplyDetail, setSdmApplyDetail] = useState<DemandCompBizType>({} as DemandCompBizType);
|
||||
const [transformedList, setTransformedList] = useState<DemandCompBizType[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log('sessionStorage : ',sessionStorage.getItem('user'));
|
||||
const user = sessionStorage.getItem('user');
|
||||
if (user) {
|
||||
setIsLoginStorageInfo(true);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (successLogin || isLoginStorageInfo) {
|
||||
setIsLogin(true);
|
||||
const user = sessionStorage.getItem('user');
|
||||
if (user) {
|
||||
try {
|
||||
const userData = JSON.parse(user);
|
||||
getSdmInfoStatusList(userData.dmdInfoId);
|
||||
} catch (error) {
|
||||
console.error('Error parsing user data:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [successLogin, getSdmInfoStatusList, isLoginStorageInfo]);
|
||||
|
||||
// 데이터 변환을 위한 별도의 useEffect
|
||||
useEffect(() => {
|
||||
if (sdmInfoStatusList.length > 0) {
|
||||
|
||||
|
||||
const newTransformedList = sdmInfoStatusList.map((item: DemandCompBizType) => ({
|
||||
...item,
|
||||
ctpvCd: selectSidoData.find(sido => sido.val === item.ctpvCd?.trim())?.text || '',
|
||||
sggCd: selectSigunData.find(sigun => sigun.val === item.sggCd?.trim())?.text || '',
|
||||
}));
|
||||
setTransformedList(newTransformedList);
|
||||
}
|
||||
}, [sdmInfoStatusList, selectSidoData, selectSigunData]);
|
||||
|
||||
const onClickSdmStatus = (index: number | undefined) => {
|
||||
if (index !== undefined) {
|
||||
const selectedStatus = transformedList[index];
|
||||
if (selectedStatus) {
|
||||
|
||||
|
||||
setSdmApplyDetail(selectedStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Title title="수요신청현황" />
|
||||
<ContentsDiv>
|
||||
<ListDiv>
|
||||
<div className="overflow-y-scroll h-[250px]">
|
||||
<div className="h-[110%] w-[99%]">
|
||||
<UiTable
|
||||
headName='수요정보 신청현황'
|
||||
tableData={transformedList}
|
||||
headlist={upHeadSdmInfoStatus}
|
||||
pageSize={0}
|
||||
total={0}
|
||||
cellClick={(index) => onClickSdmStatus(index)}
|
||||
onPageChange={() => {}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</ListDiv>
|
||||
<ListDiv>
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium text-label">수요신청 상세현황</h3>
|
||||
</div>
|
||||
<div className={`card !m-[1.2rem] !p-0r`}>
|
||||
<Table variant="secondary" style={{ tableLayout: 'fixed', width: '100%' }}>
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>요구시기</TableCell>
|
||||
<TableCell colSpan={2} className={TABLE_STYLES.cell}>{sdmApplyDetail?.dmndTmng?.toString()}</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>요구수량(m³/일)</TableCell>
|
||||
<TableCell colSpan={2} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.dmndQty} format="number"/>}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>사용용도</TableCell>
|
||||
<TableCell colSpan={2} className={TABLE_STYLES.cell}>{sdmApplyDetail?.useUsgCd}</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>희망단가(원/m³)</TableCell>
|
||||
<TableCell colSpan={2} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.wntUntprc} format="number"/>}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell colSpan={6} className={TABLE_STYLES.fullWidth}>요구수량</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>총대장균군수(개/100ml)</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{ sdmApplyDetail?.grssClfg!=-1?<TransValueFormat value={sdmApplyDetail?.grssClfg ?? ''} format="number"/>:''} {sdmApplyDetail?.grssClfg?sdmApplyDetail.grssClfg!=-1? '이하':'불검출':'-'} </TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>결합잔류염소(mg/L)</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.combResdch ?? ''} format="number"/>} {sdmApplyDetail?.combResdch?'이하':'-'}</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>탁도(NTU)</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.trbt ?? ''} format="number"/>} {sdmApplyDetail?.trbt?'이하':'-'}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>생물학적 산소요구량(BOD)(mg/L)</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.blgcOxygDmndQnt ?? ''} format="number"/>} {sdmApplyDetail?.blgcOxygDmndQnt?'이하':'-'}</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>냄새</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.smll} format="number"/>} {sdmApplyDetail?.smll?'이하':'-'}</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>색도(도)</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.crmty ?? ''} format="number"/>} {sdmApplyDetail?.crmty?'이하':'-'}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>총질소(T-N)(mg/L)</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.tn ?? ''} format="number"/>} {sdmApplyDetail?.tn?'이하':'-'}</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>총인(T-P)(mg/L)</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.tph ?? ''} format="number"/>} {sdmApplyDetail?.tph?'이하':'-'}</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>수소이온농도(pH)</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.ph} format="number"/>}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>염화물(mgCL/L)</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.clIonQnt ?? ''} format="number"/>} {sdmApplyDetail?.clIonQnt?'이하':'-'}</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>전기전도도(㎲/cm)</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{<TransValueFormat value={sdmApplyDetail?.cdtnv ?? ''} format="number"/>} {sdmApplyDetail?.cdtnv?'이하':'-'}</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>비고</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{sdmApplyDetail?.rmrk}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell colSpan={6} className={TABLE_STYLES.fullWidth}>공급처 후보지</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>후보지1</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{ sdmApplyDetail?.splyCndtList?.[0]?.ruseUtztnFcltNm ?? ""}</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>후보지2</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{sdmApplyDetail?.splyCndtList?.[1]?.ruseUtztnFcltNm ?? ""}</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>후보지3</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.cell}>{sdmApplyDetail?.splyCndtList?.[2]?.ruseUtztnFcltNm ?? ""}</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</ListDiv>
|
||||
<ListDiv>
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium text-label">수요신청 진행상태</h3>
|
||||
</div>
|
||||
<div className={`card !m-[1.2rem] !p-0r`}>
|
||||
<Table variant="secondary" style={{ tableLayout: 'fixed', width: '100%' }}>
|
||||
<TableBody>
|
||||
<TableRow>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>진행상태</TableCell>
|
||||
<TableCell colSpan={2} className={TABLE_STYLES.cell}>{sdmApplyDetail?.status?.toString()}</TableCell>
|
||||
<TableCell colSpan={1} className={TABLE_STYLES.header}>공급처매칭결과</TableCell>
|
||||
<TableCell colSpan={2} className={TABLE_STYLES.cell}>{sdmApplyDetail?.ruseUtztnFcltNm??''}</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</ListDiv>
|
||||
</ContentsDiv>
|
||||
{!isLogin?
|
||||
<UiModal
|
||||
body={<SdmLogin/>}
|
||||
bodyClass="sm:max-w-[90vw] w-[530px] h-[430px]"
|
||||
open={!isLogin}
|
||||
onOpenChange={(open) => {
|
||||
if(!open){
|
||||
setIsLogin(false)
|
||||
router.push('/');
|
||||
}
|
||||
}}
|
||||
noHeaderYn="Y"
|
||||
noFooterYn="Y"
|
||||
/>
|
||||
:null}
|
||||
<Toaster position="top-center" />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import { useDmStore } from '@/app/store';
|
||||
import type { MoneyDetailType, SimilarCaseType } from '@/app/store/sdmMng';
|
||||
import { SelectBox, Title, UiTable } from '@common/business_components/ui';
|
||||
import { HeadMakeColSpan } from '@common/business_components/ui/table/HeadMakeColSpan';
|
||||
import { Table, TableBody, TableCell, TableHeader, TableRow } from '@common/components/ui/table';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
type modalType = {
|
||||
sply: number | 0;
|
||||
usageCd: string | '';
|
||||
money: number | 0;
|
||||
};
|
||||
|
||||
const searchUseTypeList = [
|
||||
{ text: '공급량', val: 'S' },
|
||||
{ text: '사용용도', val: 'U' },
|
||||
];
|
||||
|
||||
export default function ModalSimilarPage({ sply = 0, usageCd = '', money = 0 }: modalType) {
|
||||
const { Sdm, getSimilarCaseList } = useDmStore((state) => state);
|
||||
const [searchUseType, setSearchUseType] = useState<{text: string, value: string}>({text: '공급량', value: 'S'});
|
||||
const [moneyDetailList, setMoneyDetailList] = useState<MoneyDetailType[]>([]);
|
||||
const {
|
||||
upHeadSimilarCaseList,
|
||||
similarCaseList,
|
||||
upHeadMoneyDetailList,
|
||||
} = Sdm;
|
||||
|
||||
const rowSpans = useMemo(() => {
|
||||
const spans: { [key: number]: number } = {};
|
||||
let currentValue = '';
|
||||
let currentSpan = 0;
|
||||
let startIndex = 0;
|
||||
|
||||
similarCaseList.forEach((item: SimilarCaseType, index: number) => {
|
||||
if (item.ruseBizNm === currentValue
|
||||
|| item.bizBgngDate === currentValue
|
||||
|| item.bizEndDate === currentValue
|
||||
|| item.reqBizReuseFcltCst === currentValue) {
|
||||
currentSpan++;
|
||||
spans[startIndex] = currentSpan;
|
||||
} else {
|
||||
currentValue = item.ruseBizNm;
|
||||
currentSpan = 1;
|
||||
startIndex = index;
|
||||
spans[index] = 1;
|
||||
}
|
||||
});
|
||||
|
||||
return spans;
|
||||
}, [similarCaseList]);
|
||||
|
||||
const handleCellClick = (index: number) => {
|
||||
setMoneyDetailList([{
|
||||
reqBizGrssCst: similarCaseList[index]?.reqBizGrssCst ?? '',
|
||||
reqBizReuseFcltCst: similarCaseList[index]?.reqBizReuseFcltCst ?? '',
|
||||
reqBizPplCst: similarCaseList[index]?.reqBizPplCst ?? '',
|
||||
reqBizArchCst: similarCaseList[index]?.reqBizArchCst ?? '',
|
||||
reqBizMchnCst: similarCaseList[index]?.reqBizMchnCst ?? '',
|
||||
reqBizMtrngCst: similarCaseList[index]?.reqBizMtrngCst ?? '',
|
||||
reqBizGisCst: similarCaseList[index]?.reqBizGisCst ?? '',
|
||||
reqBizCctvCst: similarCaseList[index]?.reqBizCctvCst ?? '',
|
||||
reqBizCmcnCst: similarCaseList[index]?.reqBizCmcnCst ?? '',
|
||||
reqBizWstCst: similarCaseList[index]?.reqBizWstCst ?? '',
|
||||
reqBizDesignCst: similarCaseList[index]?.reqBizDesignCst ?? '',
|
||||
reqBizSpvsCst: similarCaseList[index]?.reqBizSpvsCst ?? '',
|
||||
reqBizSbsdCst: similarCaseList[index]?.reqBizSbsdCst ?? '',
|
||||
reqBizEtc: similarCaseList[index]?.reqBizEtc ?? '',
|
||||
prcmPlanNlep: similarCaseList[index]?.prcmPlanNlep ?? '',
|
||||
prcmPlanLcep: similarCaseList[index]?.prcmPlanLcep ?? '',
|
||||
prcmPlanBrdnCst: similarCaseList[index]?.prcmPlanBrdnCst ?? '',
|
||||
prcmPlanEcqrRt: similarCaseList[index]?.prcmPlanEcqrRt ?? '',
|
||||
prcmPlanGrssCst: similarCaseList[index]?.prcmPlanGrssCst ?? '',
|
||||
clclnNlep: similarCaseList[index]?.clclnNlep ?? '',
|
||||
clclnLcep: similarCaseList[index]?.clclnLcep ?? '',
|
||||
clclnBrdnCst: similarCaseList[index]?.clclnBrdnCst ?? '',
|
||||
clclnEcqrRt: similarCaseList[index]?.clclnEcqrRt ?? '',
|
||||
clclnGrssCst: similarCaseList[index]?.clclnGrssCst ?? ''
|
||||
}]);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const type = searchUseType.value;
|
||||
getSimilarCaseList(type, type === 'S' ? sply : type === 'U' ? usageCd : money);
|
||||
}, [getSimilarCaseList, money, searchUseType, sply, usageCd]);
|
||||
|
||||
return (
|
||||
<div className="align-top w-[1360px] h-full">
|
||||
<Title title="유사사례 조회" />
|
||||
<div className="overflow-x-auto h-[480px] w-full mt-8" tabIndex={0} aria-label="유사사례 테이블 영역">
|
||||
<div className="w-[150%]">
|
||||
<div className="card-header justify-self-start">
|
||||
<h3 className="text-2xl font-medium text-label">유사사례</h3>
|
||||
<SelectBox
|
||||
tabIndex={0}
|
||||
aria-label="검색 기준 선택"
|
||||
selectArray={searchUseTypeList}
|
||||
value={searchUseType.value}
|
||||
onSelectValue={(val) => {
|
||||
setSearchUseType({text: searchUseTypeList.find(item => item.val === val)?.text ?? '', value: val.toString()});
|
||||
}}
|
||||
className="w-[140px] ml-20"
|
||||
label=""
|
||||
selectClass="w-[140px]"
|
||||
/>
|
||||
</div>
|
||||
<div className={`card !m-[1.2rem] !p-0r`}>
|
||||
<Table variant="secondary">
|
||||
<TableHeader className="font-bold">
|
||||
<HeadMakeColSpan upHeadList={upHeadSimilarCaseList} />
|
||||
</TableHeader>
|
||||
<TableBody className='text-center'>
|
||||
{similarCaseList.length > 0 ? similarCaseList?.map((el: SimilarCaseType, index) => (
|
||||
<TableRow key={`similar-case-row-${index}-${el.ruseBizNm}-${el.bizBgngDate}`}>
|
||||
{upHeadSimilarCaseList.map((head) => (
|
||||
head.id && head.id.length > 1 && (
|
||||
<TableCell
|
||||
tabIndex={head.id === 'someClickableId' ? 0 : undefined}
|
||||
role="button"
|
||||
aria-label={`유사사례 ${head.id} 클릭 시 상세 정보 표시`}
|
||||
className={`${head.align === 'right' ? 'text-right' : head.align === 'center' ? 'text-center' : typeof el[head.id as keyof SimilarCaseType] === 'number' ? 'text-right' : 'text-center'} cursor-pointer`}
|
||||
key={`similar-case-cell-${head.id}-${index}-${el.ruseBizNm}`}
|
||||
rowSpan={(head.id === 'businessName' || head.id ==='businessDuration' || head.id === 'money') ? rowSpans[index] : undefined}
|
||||
style={(head.id === 'businessName' || head.id === 'businessDuration' || head.id === 'money') && index > 0 && similarCaseList[index - 1]?.ruseBizNm === el.ruseBizNm ? { display: 'none' } : undefined}
|
||||
onClick={() => handleCellClick(index)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
handleCellClick(index);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{typeof el[head.id as keyof SimilarCaseType] !== 'number'
|
||||
? String(el[head.id as keyof SimilarCaseType] ?? '')
|
||||
: String(el[head.id as keyof SimilarCaseType]).includes('.')
|
||||
? String(el[head.id as keyof SimilarCaseType]).split('.').map((part, index) =>
|
||||
index === 0 ? part.replace(/\B(?=(\d{3})+(?!\d))/g, ",") : part
|
||||
).join('.')
|
||||
: String(el[head.id as keyof SimilarCaseType] ?? '').replace(/\B(?=(\d{3})+(?!\d))/g, ",")}
|
||||
</TableCell>
|
||||
)
|
||||
))}
|
||||
</TableRow>
|
||||
)) : <></>}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="overflow-x-auto h-[270px] w-full mt-8 overflow-y-hidden">
|
||||
<div className="w-[130%]">
|
||||
<UiTable
|
||||
headName={"사업비 상세정보"}
|
||||
tableData={moneyDetailList}
|
||||
headlist={upHeadMoneyDetailList}
|
||||
pageSize={0}
|
||||
total={0}
|
||||
cellClick={undefined}
|
||||
onPageChange={() => {}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,294 +0,0 @@
|
||||
"use client";
|
||||
|
||||
/**
|
||||
* 수요정보등록 화면
|
||||
*
|
||||
* 🔗 사용 API
|
||||
* - 수요정보등록: POST /reuseFc/request/demandInfo
|
||||
*/
|
||||
import { useDmStore } from "@/app/store";
|
||||
import { sdmInfoFormSchema, type DemandCompBizType, type SdmInfoFormData } from "@/app/store/sdmMng";
|
||||
import { SdmInfoRegForm } from "@/components/ui/sdmInfoInsertForm/component";
|
||||
import { UiModal } from "@common/business_components/ui/dialog_ui";
|
||||
import { Button } from "@common/components/ui";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { Suspense, useEffect, useState } from "react";
|
||||
import { FormProvider, useForm, type SubmitHandler } from "react-hook-form";
|
||||
import toast, { Toaster } from "react-hot-toast";
|
||||
import SdmLogin from "../sdmLogin/page";
|
||||
|
||||
|
||||
|
||||
function SdmInfoInsertContent() {
|
||||
const searchParams = useSearchParams();
|
||||
const { insertSdmInfoData, Sdm, setSubsInfo } = useDmStore((state) => state);
|
||||
const { successLogin } = Sdm;
|
||||
const [ismoneyDisabled, setIsmoneyDisabled] = useState(false);
|
||||
const formSchema = sdmInfoFormSchema;
|
||||
|
||||
const [isLogin, setIsLogin] = useState(false);
|
||||
const [facilityName, setFacilityName] = useState<string>("");
|
||||
const [defaultValues, setDefaultValues] = useState<SdmInfoFormData>({
|
||||
sdmNm: "",
|
||||
demandYmd: new Date(),
|
||||
demandQnty: "",
|
||||
usePurpose: "",
|
||||
money: "",
|
||||
/**요구수질 */
|
||||
reqWaterQlty: "",
|
||||
grssClfg: null,
|
||||
tn: null,
|
||||
combResdch: null,
|
||||
tph: null,
|
||||
trbt: null,
|
||||
ph: null,
|
||||
bod: null,
|
||||
/**컬럼확인필요 */
|
||||
clIonQnt: null,
|
||||
smll: "",
|
||||
cdtnv: null,
|
||||
crmty: null,
|
||||
rmrk: "",
|
||||
/** 후보지 제목 */
|
||||
candidateTitle: [
|
||||
{ ruseUtztnFcltNm: "", ruseUtztnFcltCd: "" },
|
||||
{ ruseUtztnFcltNm: "", ruseUtztnFcltCd: "" },
|
||||
{ ruseUtztnFcltNm: "", ruseUtztnFcltCd: "" },
|
||||
],
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const user = sessionStorage.getItem("user");
|
||||
|
||||
|
||||
setDefaultValues((prev) => ({
|
||||
...prev,
|
||||
sdmNm: searchParams.get("dmdCoNm") || "",
|
||||
}));
|
||||
if (searchParams.get("dmdInfoId")) {
|
||||
// const data = {
|
||||
// dmdCoNm: searchParams.get("dmdCoNm") || '',
|
||||
// ctpvCd: searchParams.get("ctpvCd") || '',
|
||||
// sggCd: searchParams.get("sggCd") || '',
|
||||
// dmdInfoId: searchParams.get("dmdInfoId") || '',
|
||||
// daddr: searchParams.get("daddr") || '',
|
||||
// telno: searchParams.get("telno") || '',
|
||||
// }
|
||||
|
||||
// sessionStorage.setItem("user", JSON.stringify(data || {}));
|
||||
}
|
||||
//sessionStorage.setItem("user", JSON.parse(user||'') || "");
|
||||
setIsLogin(user || searchParams.get("dmdInfoId") ? true : false);
|
||||
}, [searchParams]);
|
||||
|
||||
useEffect(() => {
|
||||
const local = localStorage.getItem("user");
|
||||
if (local) {
|
||||
setIsLogin(true);
|
||||
}
|
||||
setSubsInfo([]); // subsInfo 초기화 후보지 처음 화면 들어올때 초기화(스토어내 객체만)
|
||||
}, [setSubsInfo]);
|
||||
|
||||
const form = useForm<SdmInfoFormData>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues,
|
||||
mode: "onSubmit",
|
||||
});
|
||||
|
||||
const subsInfo = useDmStore((state) => state.map.subsInfo);
|
||||
|
||||
useEffect(() => {
|
||||
//map.subsInfo 스토어 객체 값이 변경되면 해당 값이 폼에 자동으로 들어가도록 수정 대신 값이 있어야함
|
||||
if (subsInfo.length > 0) {
|
||||
form.setValue("candidateTitle", subsInfo);
|
||||
}
|
||||
}, [subsInfo, form]);
|
||||
|
||||
//후보지 팝업 값 셋팅
|
||||
useEffect(() => {
|
||||
const handleMessage = (event: MessageEvent) => {
|
||||
if (event.data?.type === "SET_SUBS_INFO") {
|
||||
setSubsInfo(event.data.payload); // zustand로 값 설정
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("message", handleMessage);
|
||||
return () => window.removeEventListener("message", handleMessage);
|
||||
}, [setSubsInfo]);
|
||||
|
||||
// 로그인 성공 시 모달 닫기 및 폼 업데이트
|
||||
useEffect(() => {
|
||||
if (successLogin) {
|
||||
setIsLogin(true);
|
||||
const user = sessionStorage.getItem("user");
|
||||
if (user) {
|
||||
try {
|
||||
const userData = JSON.parse(user);
|
||||
if (userData.dmdCoNm) {
|
||||
setFacilityName(userData.dmdCoNm);
|
||||
form.setValue("sdmNm", userData.dmdCoNm); // 수요처명(회사명)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error parsing user data:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [successLogin, form]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log("Form state changed:", form.formState.errors);
|
||||
}, [form.formState.errors]);
|
||||
|
||||
// 체크박스 상태가 변경될 때마다 money 필드의 에러를 초기화
|
||||
useEffect(() => {
|
||||
if (ismoneyDisabled) {
|
||||
form.clearErrors("money");
|
||||
form.setValue("money", "");
|
||||
}
|
||||
}, [ismoneyDisabled, form]);
|
||||
const router = useRouter();
|
||||
const onSubmit: SubmitHandler<SdmInfoFormData> = async (data) => {
|
||||
try {
|
||||
const dmSubsInfo = data.candidateTitle?.filter(
|
||||
(item) => item.ruseUtztnFcltNm && item.ruseUtztnFcltCd
|
||||
);
|
||||
// Transform form data to madata.candidateTitletch API schema
|
||||
const apiData: DemandCompBizType = {
|
||||
dmdInfoId:
|
||||
JSON.parse(
|
||||
sessionStorage.getItem("user") || "{}"
|
||||
)?.dmdInfoId?.toString() ||
|
||||
searchParams.get("dmdInfoId") + (searchParams.get("bznmTotNo") || ""),
|
||||
dmdInfoFk:
|
||||
JSON.parse(
|
||||
sessionStorage.getItem("user") || "{}"
|
||||
)?.dmdInfoId?.toString() ||
|
||||
searchParams.get("dmdInfoId") + (searchParams.get("bznmTotNo") || ""),
|
||||
dmdCoNm:
|
||||
JSON.parse(
|
||||
sessionStorage.getItem("user") || "{}"
|
||||
)?.dmdCoNm?.toString() || data.sdmNm,
|
||||
ctpvCd:
|
||||
JSON.parse(
|
||||
sessionStorage.getItem("user") || "{}"
|
||||
)?.ctpvCd?.toString() ||
|
||||
searchParams.get("ctpvCd") ||
|
||||
"",
|
||||
sggCd:
|
||||
JSON.parse(
|
||||
sessionStorage.getItem("user") || "{}"
|
||||
)?.sggCd?.toString() ||
|
||||
searchParams.get("sggCd") ||
|
||||
"",
|
||||
daddr:
|
||||
JSON.parse(
|
||||
sessionStorage.getItem("user") || "{}"
|
||||
)?.daddr?.toString() ||
|
||||
searchParams.get("daddr") ||
|
||||
"",
|
||||
telno:
|
||||
JSON.parse(
|
||||
sessionStorage.getItem("user") || "{}"
|
||||
)?.telno?.toString() ||
|
||||
searchParams.get("telno") ||
|
||||
"",
|
||||
rgtr:
|
||||
JSON.parse(
|
||||
sessionStorage.getItem("user") || "{}"
|
||||
)?.rgtr?.toString() ||
|
||||
searchParams.get("rgtr") ||
|
||||
"",
|
||||
useUsgCd: data.usePurpose || "",
|
||||
wntUntprc: Number(data.money) || "",
|
||||
dmndTmng: data.demandYmd || new Date(),
|
||||
dmndQty: Number(data.demandQnty) || "",
|
||||
grssClfg: data.grssClfg === null ? -1 : Number(data.grssClfg),
|
||||
tn: Number(data.tn) || null,
|
||||
combResdch: Number(data.combResdch) || null,
|
||||
tph: Number(data.tph) || null,
|
||||
trbt: Number(data.trbt) || null,
|
||||
ph: data.ph || "",
|
||||
blgcOxygDmndQnt: Number(data.bod) || null,
|
||||
clIonQnt: Number(data.clIonQnt) || null,
|
||||
smll: data.smll || "",
|
||||
cdtnv: Number(data.cdtnv) || null,
|
||||
crmty: Number(data.crmty) || null,
|
||||
rmrk: data.rmrk || "",
|
||||
bzmnNo: "",
|
||||
dmdAplyPswd: "",
|
||||
index: 0,
|
||||
reqDt: "",
|
||||
dmdInfoSn: 0,
|
||||
status: "",
|
||||
splyCndtList: dmSubsInfo || [],
|
||||
};
|
||||
|
||||
await insertSdmInfoData(apiData);
|
||||
setTimeout(() => {
|
||||
router.push("/");
|
||||
}, 1000); // 0.8초 후 이동
|
||||
} catch (error) {
|
||||
console.error("Error submitting form:", error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-[#F2F2F2]">
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
<div className="text-center mb-8">
|
||||
<h1 className="text-4xl font-bold text-gray-900 underline">
|
||||
수요정보 등록
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div className="max-w-3xl mx-auto bg-white rounded-lg shadow-md p-6">
|
||||
<FormProvider {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
|
||||
<SdmInfoRegForm
|
||||
form={form}
|
||||
onCheckboxChange={setIsmoneyDisabled}
|
||||
facilityName={facilityName}
|
||||
/>
|
||||
<div className="flex justify-center pt-4">
|
||||
<Button
|
||||
color="sky"
|
||||
size={"sm"}
|
||||
type="submit"
|
||||
className="w-40 font-semibold py-2 px-4 rounded-md "
|
||||
>
|
||||
수요정보 등록
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</FormProvider>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!isLogin ? (
|
||||
<UiModal
|
||||
body={<SdmLogin />}
|
||||
bodyClass="sm:max-w-[90vw] w-[530px] h-[430px]"
|
||||
open={!isLogin}
|
||||
onOpenChange={(open) => {
|
||||
if (!open) {
|
||||
toast.dismiss();
|
||||
router.push("/");
|
||||
}
|
||||
}}
|
||||
noHeaderYn="Y"
|
||||
noFooterYn="Y"
|
||||
/>
|
||||
) : null}
|
||||
<Toaster position="top-center" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function MyForm() {
|
||||
return (
|
||||
<Suspense fallback={<div>Loading...</div>}>
|
||||
<SdmInfoInsertContent />
|
||||
</Suspense>
|
||||
);
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { useDmStore } from "@/app/store";
|
||||
import SdmInsertForm from '@common/business_components/ui/sdmInsertForm';
|
||||
import type { DemandCompType } from "@common/business_components/ui/sdmInsertForm/form";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useCallback, useEffect, useRef } from "react";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
|
||||
export default function MyForm() {
|
||||
const { Common, initializeCommonData, getDemandCompList, insertSdmData, Sdm } = useDmStore((state) => state);
|
||||
const { selectSidoData, selectSigunData } = Common;
|
||||
const { demandCompList, successNextPage } = Sdm;
|
||||
const router = useRouter();
|
||||
const formDataRef = useRef<DemandCompType | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
initializeCommonData();
|
||||
}, [initializeCommonData]);
|
||||
|
||||
useEffect(() => {
|
||||
if (successNextPage && formDataRef.current) {
|
||||
const data = formDataRef.current;
|
||||
console.log(data);
|
||||
const queryParams = new URLSearchParams({
|
||||
dmdCoNm: data.dmdCoNm || '',
|
||||
ctpvCd: data.ctpvCd || '',
|
||||
sggCd: data.sggCd || '',
|
||||
dmdInfoId: data.dmdInfoId || '',
|
||||
daddr: data.daddr || '',
|
||||
telno: data.telno || '',
|
||||
});
|
||||
router.push(`/pages/sdmMng/sdmInfoInsert?${queryParams.toString()}`);
|
||||
}
|
||||
}, [successNextPage, router]);
|
||||
|
||||
const callback = useCallback((searchText: string, pageCnt: number) => {
|
||||
searchText = searchText.trim();
|
||||
if (searchText.length > 0) {
|
||||
getDemandCompList(searchText, pageCnt);
|
||||
}
|
||||
}, [getDemandCompList]);
|
||||
|
||||
const handleInsertSdmData = useCallback((data: import('@common/business_components/ui/sdmInsertForm/form').DemandCompType) => {
|
||||
const convertedData: DemandCompType = {
|
||||
...data,
|
||||
};
|
||||
formDataRef.current = convertedData;
|
||||
insertSdmData(convertedData)
|
||||
}, [insertSdmData]);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-[#F2F2F2]">
|
||||
<div className="pt-12">
|
||||
<SdmInsertForm
|
||||
selectSidoData={selectSidoData}
|
||||
selectSigunData={selectSigunData}
|
||||
dataList={demandCompList}
|
||||
rtnValue={callback}
|
||||
rtnInputData={handleInsertSdmData}
|
||||
openNetwork={true}
|
||||
/>
|
||||
<Toaster position="top-center" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { SdmLoginForm } from '@/components/ui/sdmLoginForm/component';
|
||||
|
||||
export default function SdmLogin() {
|
||||
return (
|
||||
<div className="container mx-auto px-4 py-8">
|
||||
<div className="text-center mb-8">
|
||||
<h1 className="text-4xl font-bold text-gray-900 underline">
|
||||
수요처 로그인
|
||||
</h1>
|
||||
</div>
|
||||
<div className="max-w-3xl mx-auto bg-white rounded-lg shadow-md p-6">
|
||||
<SdmLoginForm />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
"use client";
|
||||
/**
|
||||
* 수요처 로그아웃 화면
|
||||
*
|
||||
* 🔗 사용 API
|
||||
*
|
||||
*/
|
||||
import { useDmStore } from "@/app/store";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useEffect } from "react";
|
||||
|
||||
export default function SdmLogout() {
|
||||
const router = useRouter();
|
||||
const { actionLogout } = useDmStore((state) => state);
|
||||
|
||||
useEffect(() => {
|
||||
actionLogout();
|
||||
router.push("/pages/sdmMng/sdmInfoInsert");
|
||||
}, [actionLogout, router]);
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -1,339 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import Image from 'next/image';
|
||||
|
||||
const NAVS = [
|
||||
{ id: 'card1', label: '물재이용이란' },
|
||||
{ id: 'card2', label: '물재이용 필요성' },
|
||||
{ id: 'card3', label: '빗물 재이용' },
|
||||
{ id: 'card4', label: '중수 재이용' },
|
||||
{ id: 'card5', label: '하수 재이용' },
|
||||
{ id: 'card6', label: '물재이용 목표' },
|
||||
];
|
||||
|
||||
export default function WrAboutPage() {
|
||||
const [activeIdx, setActiveIdx] = useState(0);
|
||||
const navRefs = useRef<(HTMLLIElement | null)[]>([]);
|
||||
const cardRefs = useRef<(HTMLElement | null)[]>([]);
|
||||
|
||||
// 클릭 시 이동
|
||||
const handleNavClick = (idx: number) => {
|
||||
setActiveIdx(idx);
|
||||
const targetCard = cardRefs.current[idx];
|
||||
if (targetCard) {
|
||||
targetCard.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
};
|
||||
|
||||
// 키보드 이벤트 처리
|
||||
const handleKeyDown = (e: React.KeyboardEvent, idx: number) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
handleNavClick(idx);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const observerOptions = {
|
||||
root: null,
|
||||
rootMargin: '-200px 0px 0px 0px', // 상단에서 200px 지점을 기준으로
|
||||
threshold: 0
|
||||
};
|
||||
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
const idx = cardRefs.current.findIndex(ref => ref === entry.target);
|
||||
if (idx !== -1) {
|
||||
setActiveIdx(idx);
|
||||
}
|
||||
}
|
||||
});
|
||||
}, observerOptions);
|
||||
|
||||
// 각 카드 요소를 관찰
|
||||
const cardsSnapshot = [...cardRefs.current];
|
||||
|
||||
cardsSnapshot.forEach((card) => {
|
||||
if (card) {
|
||||
observer.observe(card);
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
cardsSnapshot.forEach((card) => {
|
||||
if (card) {
|
||||
observer.unobserve(card);
|
||||
}
|
||||
});
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className='bg-[#F2F2F2]'>
|
||||
<main className="relative w-[950px] mx-auto pt-[100px] font-sans">
|
||||
{/* 사이드바 */}
|
||||
<nav className="fixed left-[290px] top-[195px] w-[210px] z-50" aria-label="물재이용 정보 섹션 내비게이션">
|
||||
<ul role="list" className="relative space-y-6">
|
||||
{NAVS.map((nav, idx) => (
|
||||
<li
|
||||
key={nav.id}
|
||||
ref={(el) => { navRefs.current[idx] = el; }}
|
||||
role="listitem"
|
||||
tabIndex={0}
|
||||
aria-current={idx === activeIdx ? 'true' : 'false'}
|
||||
aria-label={`현재 위치: ${nav.label}`}
|
||||
onClick={() => handleNavClick(idx)}
|
||||
onKeyDown={(e) => handleKeyDown(e, idx)}
|
||||
className={`font-NotoSansKR nav-item relative pl-6 text-2xl font-bold text-[#383838] cursor-pointer transition-all duration-300
|
||||
after:content-[''] after:absolute after:top-6 after:left-[0.5rem] after:w-[2px] after:h-[24px] after:bg-[#C5C5C5] last:after:hidden
|
||||
${idx === activeIdx ? 'text-4xl font-bold text-[#383838]' : ''}`}
|
||||
>
|
||||
<span className={`absolute left-[0.5rem] top-1 w-4 h-4 rounded-full -translate-x-1/2 transition-all duration-300
|
||||
${idx === activeIdx ? 'bg-blue-500 border-blue-500' : 'bg-white border-gray-400 border'}`}></span>
|
||||
{nav.label}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<article ref={(el) => { cardRefs.current[0] = el; }} className="card-wrap pl-[100px] mb-24 !pt-0" id="card1" aria-labelledby="card1-title">
|
||||
<h2 id="card1-title" className="font-NotoSansKR relative text-center text-4xl font-medium text-gray-900 mb-6">
|
||||
<span className="absolute left-1/2 top-[-15px] -translate-x-1/2 w-24 h-2 bg-blue-500 rounded-full"></span>
|
||||
물재이용이란
|
||||
</h2>
|
||||
<section aria-labelledby="card1-desc" className="bg-white border border-gray-300 rounded-xl p-[60px] pt-[70px] pb-[70px] ">
|
||||
<h1 id="card1-desc" className="font-NotoSansKR text-[#269DDA] text-5xl font-bold mb-8">물 재이용이 무엇인가요?</h1>
|
||||
<p className="font-NotoSansKR text-[#231815] text-2xl mb-10 leading-relaxed">
|
||||
물 재이용은 빗물, 오수, 하수처리수, 폐수처리수 및 발전소 온배수를<br />
|
||||
물 재이용시설을 이용하여 처리하고, 그 처리된 물을 생활, 공업, 농업, 조경,<br />
|
||||
하천 유지 등의 용도로 이용하는 것을 말합니다.
|
||||
</p>
|
||||
<Image
|
||||
src="/img/wrAbout/img_01_1.png"
|
||||
alt="이 이미지는 물 재이용 시스템의 개요를 나타냅니다. 가정, 공공기관, 하수처리장 등에서 발생한 생활하수, 빗물, 하수처리수를 물 재이용 시설에서 처리한 후, 청소나 화장실, 농업, 살수차, 하천 유지, 공장, 공원 및 골프장 등 다양한 용도로 활용하는 과정을 도식화한 것입니다."
|
||||
width={800}
|
||||
height={400}
|
||||
className="w-[90%] mx-auto mb-8 rounded"
|
||||
/>
|
||||
<p className="font-NotoSansKR text-center font-normal text-2xl">
|
||||
환경부는 2011년에 '물의 재이용 촉진 및 지원에 관한 법률'을 제정하여<br />
|
||||
한국환경공단과 함께 물 재이용 확대 및 촉진을 위해 노력하고 있습니다.
|
||||
</p>
|
||||
</section>
|
||||
</article>
|
||||
|
||||
{/* 카드2 */}
|
||||
<article ref={(el) => { cardRefs.current[1] = el; }} className="card-wrap pl-[100px] mb-24" id="card2" aria-labelledby="card2-title">
|
||||
<h2 id="card2-title" className="font-NotoSansKR relative text-center text-4xl font-medium text-gray-900 mb-6">
|
||||
<span className="absolute left-1/2 top-[-15px] -translate-x-1/2 w-24 h-2 bg-blue-500 rounded-full"></span>
|
||||
물재이용 필요성
|
||||
</h2>
|
||||
<section aria-labelledby="card2-desc" className="bg-white border border-gray-300 rounded-xl p-[60px] pt-[70px] pb-[70px] ">
|
||||
<h1 id="card2-desc" className="font-NotoSansKR text-[#D63F1F] text-5xl font-semibold mb-8">물 재이용, 왜 필요한가요?</h1>
|
||||
<p className="font-NotoSansKR text-2xl font-normal mb-10 leading-relaxed">
|
||||
빗물, 중수, 하수처리수는 지속적으로 이용 가능한 순환자원입니다.<br />
|
||||
물 재이용을 통한 효율적인 수자원 활용은 물 스트레스국가인 우리나라에서<br />가뭄대응능력 향상 및 하천수질보전을 위해 반드시 필요한 일입니다.
|
||||
</p>
|
||||
<Image
|
||||
src="/img/wrAbout/img_02_1.png"
|
||||
alt="이 이미지는 기후변화로 인한 온도 상승 추이를 설명한다. 왼쪽에는 지구 그림과 온도계가 함께 표시되어 있으며, 지구 평균 기온은 지난 100년간 약 0.9℃ 상승했음을 나타낸다. 오른쪽에는 대한민국 지도와 함께 온도계가 있으며, 지난 100년간 약 1.8℃ 상승했음을 보여준다. 이는 지구 평균보다 2배 높은 수치이며, 자료 출처는 「기후변화 평가보고서 2020, 환경부·기상청」이다."
|
||||
width={800}
|
||||
height={400}
|
||||
className="w-[90%] mx-auto mb-8 w-[80%]"
|
||||
/>
|
||||
<p className="font-NotoSansKR text-2xl mb-10 leading-relaxed">
|
||||
- 우리나라의 기후변화 진행속도는 세계 평균을 상회하고 있습니다.<br />
|
||||
- 기후변화로 인한 국지적인 가뭄은 더욱 자주, 더욱 심하게 일어나고 있으며, 우리의 물 안보를 위협하고 있습니다.
|
||||
</p>
|
||||
<Image
|
||||
src="/img/wrAbout/img_02_2.png"
|
||||
alt="세계 평균 1인당 강수량은 15,044m³이며, 대한민국은 2,546m³로 약 1/6 수준입니다. 계절별 강수량은 봄 10%, 여름 70%, 가을 15%, 겨울 5%입니다. 이는 여름철에 강수량이 집중되는 문제를 나타냅니다."
|
||||
width={800}
|
||||
height={400}
|
||||
className="w-[90%] mx-auto mb-8"
|
||||
/>
|
||||
<p className="font-NotoSansKR text-2xl mb-10 leading-relaxed">
|
||||
- 우리나라의 1인당 강수량은 2,546㎥ 으로 세계 평균 15,044㎥의 약 1/6 수준입니다.<br />
|
||||
- 우리나라 강수량의 70%는 여름철에 집중되어 있어 이용 가능한 수자원확보가 어렵습니다.
|
||||
</p>
|
||||
<Image
|
||||
src="/img/wrAbout/img_02_3.png"
|
||||
alt="물 재이용은 환경을 보호하며 지속 가능한 발전이 가능하게 하고, 안정적인 수자원 확보, 깨끗하고 경제적인 공업용수 공급, 하천 수질 개선을 통한 생태계 회복에 기여합니다."
|
||||
width={800}
|
||||
height={400}
|
||||
className="w-[93%] mx-auto"
|
||||
/>
|
||||
</section>
|
||||
</article>
|
||||
|
||||
{/* 카드3 */}
|
||||
<article ref={(el) => { cardRefs.current[2] = el; }} className="card-wrap pl-[100px] mb-24" id="card3" aria-labelledby="card3-title">
|
||||
<h2 id="card3-title" className="font-NotoSansKR relative text-center text-4xl font-medium text-gray-900 mb-6">
|
||||
<span className="absolute left-1/2 top-[-15px] -translate-x-1/2 w-24 h-2 bg-blue-500 rounded-full"></span>
|
||||
빗물 재이용
|
||||
</h2>
|
||||
<section aria-labelledby="card3-desc" className="bg-white border border-gray-300 rounded-xl p-[60px] pt-[70px] pb-[70px] ">
|
||||
<h1 id="card3-desc" className="font-NotoSansKR leading-snug text-[#269DDA] text-5xl font-bold mb-6">친환경 수자원 빗물,<br />편리하게 사용할 수 있어요!</h1>
|
||||
<p className="font-NotoSansKR text-2xl mb-10 leading-relaxed">가뭄을 극복하고 홍수를 예방하기 위해<br />비가 내릴 때 빗물을 모아두었다가 사용합니다.</p>
|
||||
<Image
|
||||
src="/img/wrAbout/img_03_1.png"
|
||||
alt="주택 지붕에 내린 빗물을 저장조에 모아 조경용수, 화장실용수, 세척·살수용수로 재활용하는 과정을 보여줍니다."
|
||||
width={800}
|
||||
height={400}
|
||||
className="w-[90%] mx-auto mb-6"
|
||||
/>
|
||||
<Image
|
||||
src="/img/wrAbout/img_03_2.png"
|
||||
alt="빗물이용"
|
||||
width={200}
|
||||
height={100}
|
||||
className="mb-5 w-[20%]"
|
||||
/>
|
||||
<p className="font-NotoSansKR text-2xl leading-relaxed">
|
||||
- 의무 설치대상: 지붕면적 1,000㎡ 이상의 운동장, 체육관, 공공업무시설 등<br />
|
||||
- 설 치 기 준: 집수시설, 처리시설, 빗물 저류조(지붕 집수면적 x 0.05m 이상)<br />
|
||||
- 활 용 용 도: 골프장 등의 조경용수, 화장실용수, 세척·살수용수 등
|
||||
</p>
|
||||
</section>
|
||||
</article>
|
||||
|
||||
{/* 카드4 */}
|
||||
<article ref={(el) => { cardRefs.current[3] = el; }} className="card-wrap pl-[100px] mb-24" id="card4" aria-labelledby="card4-title">
|
||||
<h2 id="card4-title" className="font-NotoSansKR relative text-center text-4xl font-medium text-gray-900 mb-6">
|
||||
<span className="absolute left-1/2 top-[-15px] -translate-x-1/2 w-24 h-2 bg-blue-500 rounded-full"></span>
|
||||
중수 재이용
|
||||
</h2>
|
||||
<section aria-labelledby="card4-desc" className="bg-white border border-gray-300 rounded-xl p-[60px] pt-[70px] pb-[70px] ">
|
||||
<h1 id="card4-desc" className="font-NotoSansKR leading-snug text-[#9771B0] text-5xl font-bold mb-6">요모조모 쓸모 많은 중수,<br />알뜰하게 재이용할 수 있어요!</h1>
|
||||
<p className="font-NotoSansKR text-2xl mb-10 leading-relaxed">한번 사용한 물을 중수도 시설을 통해 재생하여<br />개별적 또는 지역적으로 재이용할 수 있습니다.</p>
|
||||
<Image
|
||||
src="/img/wrAbout/img_04_1.png"
|
||||
alt="욕실, 세면대, 주방, 세탁기 등에서 발생한 생활용수를 중수도 시설에서 처리하여 화장실 변기용수와 조경용수로 재이용하는 시스템 흐름도입니다."
|
||||
width={800}
|
||||
height={400}
|
||||
className="w-[90%] mx-auto mb-6"
|
||||
/>
|
||||
<Image
|
||||
src="/img/wrAbout/img_04_2.png"
|
||||
alt="중수재이용"
|
||||
width={200}
|
||||
height={100}
|
||||
className="mb-5 w-[20%]"
|
||||
/>
|
||||
<p className="font-NotoSansKR text-2xl leading-relaxed">
|
||||
- 의무 설치대상: 일정규모 이상의 대규모점포, 숙박/목욕장, 공장, 각종 개발사업 등<br />
|
||||
- 의무 재이용량: 물 사용량의 10% 이상<br />
|
||||
- 활 용 용 도: 청소·화장실용수, 세척·살수용수, 조경용수, 공업용수 등
|
||||
</p>
|
||||
</section>
|
||||
</article>
|
||||
|
||||
{/* 카드5 */}
|
||||
<article ref={(el) => { cardRefs.current[4] = el; }} className="card-wrap pl-[100px] mb-24" id="card5" aria-labelledby="card5-title">
|
||||
<h2 id="card5-title" className="font-NotoSansKR relative text-center text-4xl font-medium text-gray-900 mb-6">
|
||||
<span className="absolute left-1/2 top-[-15px] -translate-x-1/2 w-24 h-2 bg-blue-500 rounded-full"></span>
|
||||
하수 재이용
|
||||
</h2>
|
||||
<section aria-labelledby="card5-desc" className="bg-white border border-gray-300 rounded-xl p-[60px] pt-[70px] pb-[70px] ">
|
||||
<h1 id="card5-desc" className="font-NotoSansKR leading-snug text-[#00818A] text-5xl font-bold mb-6">아직 할 일이 많은 하수처리수,<br />풍족하게 재이용할 수 있어요!</h1>
|
||||
<p className="font-NotoSansKR text-2xl mb-10 leading-relaxed">사용하고 버려져 공공하수처리장으로 모여든 물은<br />용도에 맞는 처리 후 재이용 할 수 있습니다.</p>
|
||||
<Image
|
||||
src="/img/wrAbout/img_05_1.png"
|
||||
alt="주택과 건물에서 발생한 하수가 공공하수처리장을 거쳐 처리된 후, 물 재이용 시설에서 정화되어 공업용수, 하천유지용수, 농업용수, 세척·살수용수로 재활용됩니다."
|
||||
width={800}
|
||||
height={400}
|
||||
className="w-[90%] mx-auto mb-6"
|
||||
/>
|
||||
<Image
|
||||
src="/img/wrAbout/img_05_2.png"
|
||||
alt="하수처리 재이용"
|
||||
width={200}
|
||||
height={100}
|
||||
className="mb-5 w-[20%]"
|
||||
/>
|
||||
<p className="font-NotoSansKR text-2xl leading-relaxed">
|
||||
- 의무 이용대상: 공공하수처리장(시설용량 5,000㎥/일 이상)<br />
|
||||
- 의무 재이용량: 일 하수처리량의 10% 이상<br />
|
||||
- 활 용 용 도: 공업용수, 하천유지용수, 농업용수 등
|
||||
</p>
|
||||
</section>
|
||||
</article>
|
||||
|
||||
{/* 카드6 */}
|
||||
<article ref={(el) => { cardRefs.current[5] = el; }} className="card-wrap pl-[100px] mb-24" id="card6" aria-labelledby="card6-title">
|
||||
<h2 id="card6-title" className="font-NotoSansKR relative text-center text-4xl font-medium text-gray-900 mb-6">
|
||||
<span className="absolute left-1/2 top-[-15px] -translate-x-1/2 w-24 h-2 bg-blue-500 rounded-full"></span>
|
||||
물재이용 목표
|
||||
</h2>
|
||||
<section aria-labelledby="card6-desc" className="bg-white border border-gray-300 rounded-xl p-[60px] pt-[70px] pb-[70px] ">
|
||||
<h1 id="card6-desc" className="font-NotoSansKR leading-snug text-[#269DDA] text-5xl font-bold mb-6">2030년 물 재이용 목표를 위해<br />모두의 관심과 노력이 필요합니다.</h1>
|
||||
<p className="font-NotoSansKR text-2xl mb-6 leading-relaxed">
|
||||
환경부는 2030년까지의 제2차 물 재이용 기본계획을 수립하여<br />한국환경공단과 함께 물 재이용 확대 및 촉진을 위해 노력하고 있습니다.
|
||||
</p>
|
||||
<Image
|
||||
src="/img/wrAbout/img_06_1.png"
|
||||
alt="지속가능한 물 재이용 정착으로 건전한 물순환 확산"
|
||||
width={800}
|
||||
height={400}
|
||||
className="w-[80%] mx-auto mb-6"
|
||||
/>
|
||||
<div className="flex gap-3 flex-wrap justify-between bg-gray-100 p-6 rounded-lg mb-8">
|
||||
<div className="flex-1 text-center">
|
||||
<Image
|
||||
src="/img/wrAbout/icon_01.png"
|
||||
alt=""
|
||||
width={70}
|
||||
height={70}
|
||||
className="mx-auto mb-4 w-[auto] h-[70px]"
|
||||
/>
|
||||
<h3 className="font-NotoSansKR text-center font-medium text-nowrap mb-2 text-2xl">도시 물 순환이용<br />체계로의 전환</h3>
|
||||
<ul className="font-NotoSansKR text-xl text-gray-600 leading-relaxed space-y-1">
|
||||
<li className="text-nowrap">- 지속가능한 물 순환이용체계 구축</li>
|
||||
<li>- 물 재이용시설 관리체계 강화</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="flex-1 text-center">
|
||||
<Image
|
||||
src="/img/wrAbout/icon_02.png"
|
||||
alt=""
|
||||
width={70}
|
||||
height={70}
|
||||
className="mx-auto mb-4 w-[auto] h-[70px]"
|
||||
/>
|
||||
<h3 className="font-NotoSansKR text-center font-medium text-nowrap mb-2 text-2xl">유역기반 하수처리수 재이용<br />수요확대 및 공급체계 구축</h3>
|
||||
<ul className="font-NotoSansKR text-xl text-gray-600 leading-relaxed space-y-1">
|
||||
<li>- 유역기반 통합물관리 연계 강화</li>
|
||||
<li>- 하수처리수 재이용 공업용수 공급 활성화</li>
|
||||
<li>- 하수처리수 재이용을 활용한 물순환 촉진</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="flex-1 text-center">
|
||||
<Image
|
||||
src="/img/wrAbout/icon_03.png"
|
||||
alt=""
|
||||
width={70}
|
||||
height={70}
|
||||
className="mx-auto mb-4 w-[auto] h-[70px]"
|
||||
/>
|
||||
<h3 className="font-NotoSansKR text-center font-medium text-nowrap mb-2 text-2xl">물 재이용 인식제고<br />및 산업발전 기반 마련</h3>
|
||||
<ul className="font-NotoSansKR text-xl text-gray-600 leading-relaxed space-y-1">
|
||||
<li>- 물 재이용 교육 및 대국민 홍보 강화</li>
|
||||
<li>- 핵심기술 육성 및 산업발전 지원 강화</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<Image
|
||||
src="/img/wrAbout/img_06_2.png"
|
||||
alt="물 재이용량은 2014년 13.3억톤에서 2030년 25.9억톤까지 증가할 것으로 예상됩니다. 그에 따른 기대 효과로는 연간 상수대체 17.9억톤, 물산업 시장성장 약 2조3,541억 원, 건천화된 73개 하천 해소, 약 20,550명의 고용 효과가 포함됩니다."
|
||||
width={800}
|
||||
height={400}
|
||||
className="w-[90%] mx-auto"
|
||||
/>
|
||||
</section>
|
||||
</article>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,364 +0,0 @@
|
||||
import Image from "next/image";
|
||||
|
||||
export default function WrExamplePage() {
|
||||
return (
|
||||
<div className="bg-[#F2F2F2]">
|
||||
<div className="relative w-[950px] mx-auto pt-[50px] font-sans">
|
||||
<h2 className="font-NotoSansKR relative text-center text-4xl font-bold text-gray-900 mb-6">
|
||||
<span className="absolute left-1/2 top-[-15px] -translate-x-1/2 w-24 h-2 rounded-full"></span>
|
||||
물 재이용사례
|
||||
</h2>
|
||||
</div>
|
||||
<div className="bg-[#F2F2F2] text-gray-800">
|
||||
<div className="relative w-[950px] mx-auto pt-[50px] font-sans">
|
||||
{/* 카드 1 */}
|
||||
<div className="card-wrap mb-24" id="card1">
|
||||
<h2 className="font-NotoSansKR relative text-center text-4xl font-bold text-gray-900 mb-6">
|
||||
<span className="absolute left-1/2 top-[-15px] -translate-x-1/2 w-24 h-2 bg-blue-500 rounded-full"></span>
|
||||
사업사례 1
|
||||
</h2>
|
||||
<div className="bg-white border border-gray-300 rounded-xl p-[60px] pt-[70px] pb-[70px]">
|
||||
{/* 사업명 */}
|
||||
<section>
|
||||
<h2 className="font-NotoSansKR text-2xl text-[#657D91] font-bold bg-[#EAEFF3] p-3 rounded mb-4">사업명</h2>
|
||||
<p className="font-NotoSansKR text-2xl font-semibold text-gray-800 leading-relaxed">
|
||||
경기권역 반도체 사업장 물 재이용 사업<br />
|
||||
: 삼성전자 반도체 사업장 하수재이용수 공급(재이용시설 설치, 공급망 구축 등)
|
||||
</p>
|
||||
</section>
|
||||
{/* 사업 배경 및 목적 */}
|
||||
<section className="mt-8">
|
||||
<h2 className="font-NotoSansKR text-2xl text-[#657D91] font-bold bg-[#EAEFF3] p-3 rounded mb-4">사업 배경 및 목적</h2>
|
||||
<p className="font-NotoSansKR text-2xl font-semibold text-gray-800 leading-relaxed">
|
||||
삼성전자는 환경경영전략 실현과 반도체 공장의 안정적 용수 공급을 위해 하수 재이용수를 공업용수로 도입하기로 결정
|
||||
</p>
|
||||
</section>
|
||||
{/* 사업 주요 내용 */}
|
||||
<section className="mt-8">
|
||||
<h2 className="font-NotoSansKR text-2xl text-[#657D91] font-bold bg-[#EAEFF3] p-3 rounded mb-4">사업 주요 내용</h2>
|
||||
<p className="font-NotoSansKR text-2xl font-semibold text-gray-800 leading-relaxed">
|
||||
1단계 사업[화성·오산(12만㎥/일) → 삼성 화성·기흥(29년 공급)]<br />
|
||||
2단계 사업[수원(21만㎥/일) → 삼성 평택(30년 공급)]
|
||||
</p>
|
||||
</section>
|
||||
{/* 특이사항 */}
|
||||
<section className="mt-8">
|
||||
<h2 className="font-NotoSansKR text-2xl text-[#657D91] font-bold bg-[#EAEFF3] p-3 rounded mb-4">특이사항</h2>
|
||||
<p className="font-NotoSansKR text-2xl font-semibold text-gray-800 leading-relaxed">
|
||||
본 사업은 광역 하수 재이용 공급망 구축의 첫 사업으로 초기 민관협의체를 구성·운영 후 관계기관(환경부, 경기도, 지자체, 공단, 수공, 삼성전자) 간 MOU 체결을 통해 사업을 추진하였으며, 반도체 사업장에 하수 재이용수를 공업용수로 공급하는 국내 첫 사업으로 위협부담 해소를 위해 사업을 단계별로 시행
|
||||
</p>
|
||||
</section>
|
||||
{/* 단계별 시각 정보 */}
|
||||
<section className="bg-white px-4 mt-8">
|
||||
<Image
|
||||
src="/img/wrExample/img_ex_01_1.png"
|
||||
alt="사업단계 설명"
|
||||
width={800}
|
||||
height={400}
|
||||
className="w-[100%] mx-auto mb-8"
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 카드2 */}
|
||||
<div className="card-wrap mb-24" id="card2">
|
||||
<h2 className="font-NotoSansKR relative text-center text-4xl font-bold text-gray-900 mb-6">
|
||||
<span className="absolute left-1/2 top-[-15px] -translate-x-1/2 w-24 h-2 bg-blue-500 rounded-full"></span>
|
||||
사업사례 2
|
||||
</h2>
|
||||
<div className="bg-white border border-gray-300 rounded-xl p-[60px] pt-[70px] pb-[70px]">
|
||||
{/* 사업명 */}
|
||||
<section>
|
||||
<h2 className="font-NotoSansKR text-2xl text-[#657D91] font-bold bg-[#EAEFF3] p-3 rounded mb-4">사업명</h2>
|
||||
<p className="font-NotoSansKR text-2xl font-semibold text-gray-800 leading-relaxed">
|
||||
경기도 파주시 하수재이용<br />
|
||||
: 운정·금촌하수처리시설의 하수처리수를 막공법(U/F, R/O)으로 재처리하여 파주 LCD 산업단지내 3개 업체*에 공업용수 공급<br /><br />
|
||||
*LG디스플레이(P10공장), 희성전자, 엔비텍 등
|
||||
</p>
|
||||
</section>
|
||||
{/* 사업 방식 */}
|
||||
<section className="mt-8">
|
||||
<h2 className="font-NotoSansKR text-2xl text-[#657D91] font-bold bg-[#EAEFF3] p-3 rounded mb-4">사업 방식</h2>
|
||||
<p className="font-NotoSansKR text-2xl font-semibold text-gray-800 leading-relaxed">
|
||||
민간투자사업(시행사 : 파주그린허브(주), 운영사 : 테크로스)
|
||||
</p>
|
||||
</section>
|
||||
{/* 사업 주요 내용 */}
|
||||
<section className="mt-8">
|
||||
<h2 className="font-NotoSansKR text-2xl text-[#657D91] font-bold bg-[#EAEFF3] p-3 rounded mb-4">사업 주요 내용</h2>
|
||||
<p className="font-NotoSansKR text-2xl font-semibold text-gray-800 leading-relaxed">
|
||||
처리공정 : 전처리분리막(U/F) + 역삼투서비(R/O)<br />
|
||||
공급량 : 4만m³/일<br />
|
||||
관로 : 13.6km<br />
|
||||
사업비 : 651억원
|
||||
</p>
|
||||
</section>
|
||||
{/* 특이사항 */}
|
||||
<section className="mt-8">
|
||||
<h2 className="font-NotoSansKR text-2xl text-[#657D91] font-bold bg-[#EAEFF3] p-3 rounded mb-4">특이사항</h2>
|
||||
<p className="font-NotoSansKR text-2xl font-semibold text-gray-800 leading-relaxed">
|
||||
하수처리수 재이용시설 자체 농축폐수 처리공정 개발 및 적용 성공사례
|
||||
</p>
|
||||
<div className="grid grid-cols-1 gap-0 items-start">
|
||||
{/* 좌측 이미지 */}
|
||||
<div>
|
||||
<Image
|
||||
src="/img/wrExample/img_ex_02_1.png"
|
||||
alt="적용 성공사례 지도"
|
||||
width={1000}
|
||||
height={1200}
|
||||
className="w-[100%] mx-auto"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 우측 표 */}
|
||||
<div className="space-y-3">
|
||||
{/* 하수처리수 공급 */}
|
||||
<div>
|
||||
<table className="min-w-full h-[500px]text-center border border-black">
|
||||
<colgroup>
|
||||
<col style={{ width: "20%" }} />
|
||||
<col style={{ width: "40%" }} />
|
||||
<col style={{ width: "40%" }} />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colSpan={3} className="font-NotoSansKR bg-[#ffc000] h-[30px] text-xl text-white font-semibold text-center pt-[6px] pb-[6px] px-0">하수처리수 공급</td>
|
||||
</tr>
|
||||
<tr className="text-xl h-[30px]">
|
||||
<td className="font-NotoSansKR text-xl h-[30px] border-black border p-1 font-bold">위치</td>
|
||||
<td className="font-NotoSansKR text-xl h-[30px] border-black border p-1 font-bold">금촌하수</td>
|
||||
<td className="font-NotoSansKR text-xl h-[30px] border-black border p-1 font-bold">운정하수</td>
|
||||
</tr>
|
||||
<tr className="text-sm">
|
||||
<td className="font-NotoSansKR text-xl h-[30px] border-black border p-1 font-bold">시설용량</td>
|
||||
<td className="font-NotoSansKR text-xl h-[30px] border-black border p-1 font-bold">44,000㎥/일</td>
|
||||
<td className="font-NotoSansKR text-xl h-[30px] border-black border p-1 font-bold">60,000㎥/일</td>
|
||||
</tr>
|
||||
<tr className="text-sm">
|
||||
<td className="font-NotoSansKR text-xl h-[30px] border-black border p-1 font-bold">유입량</td>
|
||||
<td className="font-NotoSansKR text-xl h-[30px] border-black border p-1 font-bold">27,000㎥/일<br />자연유하</td>
|
||||
<td className="font-NotoSansKR text-xl h-[30px] border-black border p-1 font-bold">30,000㎥/일<br />이송펌프장</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{/* 첫 화살표 (자연유하 / 관로이송) */}
|
||||
<div className="flex h-[30px] justify-between items-center my-3 px-4">
|
||||
<div className="flex text-xl items-center gap-1 text-orange-600 font-bold pl-6">
|
||||
<span className="font-NotoSansKR">자연유하: 161m</span>
|
||||
<Image
|
||||
src="/img/wrExample/img_arrow_down_1.png"
|
||||
alt="오른쪽 화살표"
|
||||
width={24}
|
||||
height={24}
|
||||
className="h-6 w-6"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex text-xl items-center gap-1 text-orange-600 font-bold">
|
||||
<Image
|
||||
src="/img/wrExample/img_arrow_down_1.png"
|
||||
alt="왼쪽 화살표"
|
||||
width={24}
|
||||
height={24}
|
||||
className="h-6 w-6"
|
||||
/>
|
||||
<span className="font-NotoSansKR">관로이송: 3.2km</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 물재이용 처리시설 */}
|
||||
<div>
|
||||
<table className="min-w-full text-center border border-black">
|
||||
<colgroup>
|
||||
<col style={{ width: "50%" }} />
|
||||
<col style={{ width: "50%" }} />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colSpan={2} className="font-NotoSansKR bg-[#00b050] text-xl h-[30px] text-white font-semibold text-center pt-[6px] pb-[6px] px-0">물재이용 처리시설</td>
|
||||
</tr>
|
||||
<tr className="text-sm">
|
||||
<td className="font-NotoSansKR text-xl h-[30px] border-black border p-1 font-bold">유입유량</td>
|
||||
<td className="font-NotoSansKR text-xl h-[30px] border-black border p-1 font-bold">57,000㎥/일</td>
|
||||
</tr>
|
||||
<tr className="text-sm">
|
||||
<td className="font-NotoSansKR text-xl h-[30px] border-black border p-1 font-bold">재이용시설</td>
|
||||
<td className="font-NotoSansKR text-xl h-[30px] border-black border p-1 font-bold">40,000㎥/일</td>
|
||||
</tr>
|
||||
<tr className="text-sm">
|
||||
<td className="font-NotoSansKR text-xl h-[30px] border-black border p-1 font-bold">농축폐수처리</td>
|
||||
<td className="font-NotoSansKR text-xl h-[30px] border-black border p-1 font-bold">17,000㎥/일</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{/* 두 번째 화살표 (공급관로) */}
|
||||
<div className="flex flex-col items-center my-3">
|
||||
<div className="flex h-[30px] items-center gap-1 text-xl text-blue-600 font-semibold">
|
||||
<span className="font-NotoSansKR">공급관로 L=10.4km</span>
|
||||
<Image
|
||||
src="/img/wrExample/img_arrow_down_2.png"
|
||||
alt="아래 화살표"
|
||||
width={24}
|
||||
height={24}
|
||||
className="h-6 w-6"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 수요처 공급 */}
|
||||
<div>
|
||||
<table className="min-w-full text-center border border-black">
|
||||
<colgroup>
|
||||
<col style={{ width: "50%" }} />
|
||||
<col style={{ width: "50%" }} />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colSpan={2} className="font-NotoSansKR bg-[#3399ff] h-[30px] text-xl text-white font-semibold text-center pt-[6px] pb-[6px] px-0">수요처 공급</td>
|
||||
</tr>
|
||||
<tr className="text-sm">
|
||||
<td className="font-NotoSansKR text-xl h-[30px] border-black border p-1 font-bold">수요처</td>
|
||||
<td className="font-NotoSansKR text-xl h-[30px] border-black border p-1 font-bold">LG디스플레이 외 2개사</td>
|
||||
</tr>
|
||||
<tr className="text-sm">
|
||||
<td className="font-NotoSansKR text-xl h-[30px] border-black border p-1 font-bold">수요량</td>
|
||||
<td className="font-NotoSansKR text-xl h-[30px] border-black border p-1 font-bold">40,000㎥/일</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 카드3 */}
|
||||
<div className="card-wrap mb-24" id="card3">
|
||||
<h2 className="font-NotoSansKR relative text-center text-4xl font-bold text-gray-900 mb-6">
|
||||
<span className="absolute left-1/2 top-[-15px] -translate-x-1/2 w-24 h-2 bg-blue-500 rounded-full"></span>
|
||||
사업사례 3
|
||||
</h2>
|
||||
<div className="bg-white border border-gray-300 rounded-xl p-[60px] pt-[70px] pb-[70px]">
|
||||
{/* 사업명 */}
|
||||
<section>
|
||||
<h2 className="font-NotoSansKR text-2xl text-[#657D91] font-bold bg-[#EAEFF3] p-3 rounded mb-4">사업명</h2>
|
||||
<p className="font-NotoSansKR text-2xl font-semibold text-gray-800 leading-relaxed">
|
||||
충남 보령 하수재이용<br />
|
||||
: 보령·신보령화력발전소에 하루 1만톤의 탈황용수 공급을 위해 보령하수처리시설의 하수처리수 재이용 사업 추진
|
||||
</p>
|
||||
</section>
|
||||
{/* 사업 방식 */}
|
||||
<section className="mt-8">
|
||||
<h2 className="font-NotoSansKR text-2xl text-[#657D91] font-bold bg-[#EAEFF3] p-3 rounded mb-4">사업 방식</h2>
|
||||
<p className="font-NotoSansKR text-2xl font-semibold text-gray-800 leading-relaxed">
|
||||
2017년 한국환경공단과 사업대행 위 · 수탁 협약 체결
|
||||
</p>
|
||||
</section>
|
||||
{/* 사업 주요 내용 */}
|
||||
<section className="mt-8">
|
||||
<h2 className="font-NotoSansKR text-2xl text-[#657D91] font-bold bg-[#EAEFF3] p-3 rounded mb-4">사업 주요 내용</h2>
|
||||
<p className="font-NotoSansKR text-2xl font-semibold text-gray-800 leading-relaxed">
|
||||
처리공정 : 전처리분리막(U/F) + 역삼투서비(R/O)<br />
|
||||
공급량 : 1만m3/일<br />
|
||||
관로 : 8.7km<br />
|
||||
사업비 : 315억원(국비 216억원, 재원협의(안))
|
||||
</p>
|
||||
</section>
|
||||
{/* 특이사항 */}
|
||||
<section className="mt-8">
|
||||
<h2 className="font-NotoSansKR text-2xl text-[#657D91] font-bold bg-[#EAEFF3] p-3 rounded mb-4">특이사항</h2>
|
||||
<p className="font-NotoSansKR text-2xl font-semibold text-gray-800 leading-relaxed">
|
||||
국내 유일 하수재이용 공업용수 공급 재정사업으로 2016년 충남 서부권 가뭄 시 지역사회 기여 차원에서 기관간 MOU (환경부, 충청남도, 보령시, 한국중부발전㈜)를 바탕
|
||||
으로 추진 되어 경제성보다 공익성을 바탕으로 국가전력 기반시설의 안정적 운영 도모
|
||||
</p>
|
||||
<div className="p-2">
|
||||
<Image
|
||||
src="/img/wrExample/img_ex_03_1.png"
|
||||
alt="국가전력 기반시설 설명"
|
||||
width={700}
|
||||
height={350}
|
||||
className="w-[100%] mx-auto"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 카드4 */}
|
||||
<div className="card-wrap mb-24" id="card4">
|
||||
<h2 className="font-NotoSansKR relative text-center text-4xl font-bold text-gray-900 mb-6">
|
||||
<span className="absolute left-1/2 top-[-15px] -translate-x-1/2 w-24 h-2 bg-blue-500 rounded-full"></span>
|
||||
사업사례 4
|
||||
</h2>
|
||||
<div className="bg-white border border-gray-300 rounded-xl p-[60px] pt-[70px] pb-[70px]">
|
||||
{/* 사업명 */}
|
||||
<section>
|
||||
<h2 className="font-NotoSansKR text-2xl text-[#657D91] font-bold bg-[#EAEFF3] p-3 rounded mb-4">사업명</h2>
|
||||
<p className="font-NotoSansKR text-2xl font-semibold text-gray-800 leading-relaxed">
|
||||
경기도 수원시 빗물이용과 중수도
|
||||
</p>
|
||||
</section>
|
||||
{/* 사업 내용 */}
|
||||
<section className="mt-8">
|
||||
<h2 className="font-NotoSansKR text-2xl text-[#657D91] font-bold bg-[#EAEFF3] p-3 rounded mb-4">사업 내용</h2>
|
||||
<p className="font-NotoSansKR text-2xl font-semibold text-gray-800 leading-relaxed">
|
||||
(빗물) 월드컵경기장 등 4개소에 노면 빗물분사시스템 및 빗물 공급기를 설치하여 혹서기 도시 열섬효과 및 도로 비산먼지 저감<br />
|
||||
※ 시설 가동중 도로 노면 온도의 7.2 ~ 26.3℃ 감소 효과 확인(21.7월, 수원시청).<br />
|
||||
다만, 가뭄기나 강우량이 적을 경우 빗물을 원활히 공급하기 다소 곤란<br /><br />
|
||||
- (중수도) 수원시청, 장안구청, 월드컵경기장에 빗물 연계 중수도를 화장실·조경용수 등으로 사용하여 수도요금 절감(연 7백~30백만원)
|
||||
</p>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 items-start p-2">
|
||||
<div>
|
||||
<Image
|
||||
src="/img/wrExample/img_ex_04_1.png"
|
||||
alt="월드컵경기장 인근도로 노면 살수 이미지"
|
||||
width={350}
|
||||
height={250}
|
||||
className="w-[100%] mx-auto"
|
||||
/>
|
||||
<span className="font-NotoSansKR text-xl block text-center font-semibold my-2"><월드컵경기장 인근도로 노면 살수></span>
|
||||
</div>
|
||||
<div>
|
||||
<Image
|
||||
src="/img/wrExample/img_ex_04_2.png"
|
||||
alt="주유기 모양의 빗물 공급기 이미지"
|
||||
width={350}
|
||||
height={250}
|
||||
className="w-[100%] mx-auto"
|
||||
/>
|
||||
<span className="font-NotoSansKR text-xl block text-center font-semibold my-2"><주유기 모양의 빗물 공급기></span>
|
||||
</div>
|
||||
<div>
|
||||
<Image
|
||||
src="/img/wrExample/img_ex_04_3.png"
|
||||
alt="빗물주유기에서 살수용수 공급 이미지"
|
||||
width={350}
|
||||
height={250}
|
||||
className="w-[100%] mx-auto"
|
||||
/>
|
||||
<span className="font-NotoSansKR text-xl block text-center font-semibold my-2"><빗물주유기에서 살수용수 공급></span>
|
||||
</div>
|
||||
<div>
|
||||
<Image
|
||||
src="/img/wrExample/img_ex_04_4.png"
|
||||
alt="빗물 공급기 및 빗물분사장치 위치"
|
||||
width={350}
|
||||
height={250}
|
||||
className="w-[100%] mx-auto"
|
||||
/>
|
||||
<span className="font-NotoSansKR text-xl block text-center font-semibold my-2"><빗물 공급기 및 빗물분사장치 위치></span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
import Image from "next/image";
|
||||
|
||||
export default function WrOverviewPage() {
|
||||
return (
|
||||
<div className="bg-[#F2F2F2]">
|
||||
<div className="relative w-[950px] mx-auto pt-[50px] font-sans">
|
||||
{/* 카드 1 - 물재이용 사업 추진 절차 */}
|
||||
<div className="card-wrap mb-24" id="card1">
|
||||
<h2 className="font-NotoSansKR relative text-center text-4xl font-bold text-gray-900 mb-6">
|
||||
<span className="absolute left-1/2 top-[-15px] -translate-x-1/2 w-24 h-2 bg-blue-500 rounded-full"></span>
|
||||
물재이용 사업 추진 절차
|
||||
</h2>
|
||||
<div className="bg-white border border-gray-300 rounded-xl p-[60px] py-[50px]">
|
||||
<table className="table-fixed w-full text-center text-sm">
|
||||
<caption className="sr-only">물 재이용 추진 절차 상세 표</caption>
|
||||
<colgroup>
|
||||
<col style={{ width: "28%" }} />
|
||||
<col style={{ width: "2%" }} />
|
||||
<col style={{ width: "35%" }} />
|
||||
<col style={{ width: "5%" }} />
|
||||
<col style={{ width: "30%" }} />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
{/* 1단계 */}
|
||||
<tr><td></td><td></td><td className="font-NotoSansKR text-[17px] bg-[#EAEFF3] font-bold py-5">타당성 조사</td><td></td><td></td></tr>
|
||||
<tr><td></td><td></td><td className="font-NotoSansKR text-2xl font-bold text-center">↓</td><td></td><td></td></tr>
|
||||
{/* 2단계 */}
|
||||
<tr><td className="font-NotoSansKR text-2xl bg-[#F4D62D] text-black font-bold py-5">매칭시스템 신청</td><td></td><td className="font-NotoSansKR text-[17px] bg-[#EAEFF3] font-bold py-5">사업계획(안) 수립</td><td></td><td></td></tr>
|
||||
<tr><td></td><td></td><td className="font-NotoSansKR text-2xl font-bold text-center">↓</td><td></td><td></td></tr>
|
||||
{/* 3단계 */}
|
||||
<tr><td className="font-NotoSansKR text-2xl bg-[#F4D62D] text-black font-bold py-5">하수도정보시스템 신청</td><td></td><td className="font-NotoSansKR text-[17px] bg-[#EAEFF3] font-bold py-5">국고지원 신청</td><td className="font-NotoSansKR text-2xl font-bold">→</td><td className="font-NotoSansKR text-2xl text-[#fff] bg-[#00B076] font-bold py-5">기술검토(한국환경공단)</td></tr>
|
||||
<tr><td></td><td></td><td className="font-NotoSansKR text-2xl font-bold text-center">↓</td><td></td><td></td></tr>
|
||||
{/* 4단계 */}
|
||||
<tr><td></td><td></td><td className="font-NotoSansKR text-[17px] bg-[#EAEFF3] font-bold py-5">국고지원 확정 통보</td><td></td><td></td></tr>
|
||||
<tr><td></td><td></td><td className="font-NotoSansKR text-2xl font-bold text-center">↓</td><td></td><td></td></tr>
|
||||
{/* 5단계 */}
|
||||
<tr><td></td><td></td><td className="font-NotoSansKR text-[17px] bg-[#EAEFF3] font-bold py-5">사업계획 수립(실시설계)</td><td></td><td></td></tr>
|
||||
<tr><td></td><td></td><td className="font-NotoSansKR text-2xl font-bold text-center">↓</td><td></td><td></td></tr>
|
||||
{/* 6단계 */}
|
||||
<tr><td className="font-NotoSansKR text-2xl bg-[#F4D62D] text-black font-bold py-5">(행정관) 기술검토 신청</td><td></td><td className="font-NotoSansKR text-[17px] bg-[#EAEFF3] font-bold py-5">재원협의(지방환경관서장)</td><td className="font-NotoSansKR text-2xl font-bold">→</td><td className="font-NotoSansKR text-2xl text-[#fff] bg-[#00B076] font-bold py-5">기술검토(한국환경공단)</td></tr>
|
||||
<tr><td></td><td></td><td className="font-NotoSansKR text-2xl font-bold text-center">↓</td><td></td><td></td></tr>
|
||||
{/* 7단계 */}
|
||||
<tr><td className="font-NotoSansKR text-2xl bg-[#F4D62D] text-black font-bold py-5">(행정관) 기술검토 신청</td><td></td><td className="font-NotoSansKR text-[17px] bg-[#EAEFF3] font-bold py-5">설치승인(시도지사)</td><td className="font-NotoSansKR text-2xl font-bold">→</td><td className="font-NotoSansKR text-2xl text-[#fff] bg-[#00B076] font-bold py-5">기술검토(한국환경공단)</td></tr>
|
||||
<tr><td></td><td></td><td className="font-NotoSansKR text-2xl font-bold text-center">↓</td><td></td><td></td></tr>
|
||||
{/* 8단계 */}
|
||||
<tr><td></td><td></td><td className="font-NotoSansKR text-[17px] bg-[#EAEFF3] font-bold py-5">공사시행</td><td></td><td></td></tr>
|
||||
<tr><td></td><td></td><td className="font-NotoSansKR text-2xl font-bold text-center">↓</td><td></td><td></td></tr>
|
||||
{/* 9단계 */}
|
||||
<tr><td className="font-NotoSansKR text-2xl bg-[#F4D62D] text-black font-bold py-5">(행정관) 기술검토 신청</td><td></td><td className="font-NotoSansKR text-[17px] bg-[#EAEFF3] font-bold py-5">준공(국고정산)</td><td className="font-NotoSansKR text-2xl font-bold">→</td><td className="font-NotoSansKR text-2xl text-[#fff] bg-[#00B076] font-bold py-5">기술검토(한국환경공단)</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 카드 2 - 물재이용 매칭 시스템 */}
|
||||
<div className="mt-36 card-wrap mb-24" id="card2">
|
||||
<h2 className="font-NotoSansKR relative text-center text-4xl font-bold text-gray-900 mb-6">
|
||||
<span className="absolute left-1/2 top-[-15px] -translate-x-1/2 w-24 h-2 bg-blue-500 rounded-full"></span>
|
||||
물재이용 매칭 시스템이란?
|
||||
</h2>
|
||||
<div className="bg-white border border-gray-300 rounded-xl p-[60px] pt-[70px] pb-[70px]">
|
||||
{/* 개요 */}
|
||||
<section>
|
||||
<h2 className="font-NotoSansKR text-2xl text-[#657D91] font-bold bg-[#EAEFF3] p-3 rounded mb-4">개요</h2>
|
||||
<p className="font-NotoSansKR text-2xl font-bold text-gray-800 leading-relaxed">
|
||||
기후변화 및 지자체의 개발속도 차이에 따라 물 자원의 수요-공급 불균형이 발생하여 빗물, 중수도, 하수처리 재처리수 등의 공급 여유가 있는 지자체의 재이용수를 부족한 지자체 수요처에 공급하여 불균형 해소를 통한 수자원 절약 및 물 자원 순환
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* 구성 */}
|
||||
<section className="mt-8">
|
||||
<h2 className="font-NotoSansKR text-2xl text-[#657D91] font-bold bg-[#EAEFF3] p-3 rounded mb-4">구성</h2>
|
||||
<ul className=" font-NotoSansKR list-[square] pl-6 text-2xl font-bold text-gray-800 leading-relaxed space-y-2">
|
||||
<li>공급자: 하수처리장, 빗물이용시설, 중수도 등</li>
|
||||
<li>수요자: 산업체(냉각수), 공공시설(조경, 도로청소), 농민(농업용수) 등</li>
|
||||
<li>시스템: 수질, 수량, 거리 기반 매칭, 중개</li>
|
||||
<li>운영주체: 한국환경공단</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
{/* 절차 */}
|
||||
<section className="mt-8">
|
||||
<h2 className="font-NotoSansKR text-2xl text-[#657D91] font-bold bg-[#EAEFF3] p-3 rounded mb-4">사업 주요 내용</h2>
|
||||
<p className="font-NotoSansKR text-2xl font-bold text-gray-800 leading-relaxed">
|
||||
① 공급자 등록 → ② 수요자 조건 입력 → ③ 매칭 검토 → ④ 시스템 매칭 → ⑤ 계약·공급 이행
|
||||
</p>
|
||||
</section>
|
||||
|
||||
{/* 기대효과 */}
|
||||
<section className="mt-8">
|
||||
<h2 className="font-NotoSansKR text-2xl text-[#657D91] font-bold bg-[#EAEFF3] p-3 rounded mb-4">기대효과</h2>
|
||||
<ul className="font-NotoSansKR list-[square] pl-6 text-2xl font-bold text-gray-800 leading-relaxed space-y-2">
|
||||
<li>물 자원 수요-공급 조정을 통한 효율적 분배</li>
|
||||
<li>물 재이용시설 데이터 수집으로 체계적 관리</li>
|
||||
<li>지자체, 사업시행자 등 재이용 정보 획득 용이</li>
|
||||
</ul>
|
||||
<div className="text-center my-6">
|
||||
<Image
|
||||
src="/img/wrOverview/img_ov_01_1.png"
|
||||
alt="하수 재이용 시스템 흐름도. 저장탱크에서 재처리된 물이 손 씻기, 수도, 세정 등에 공급되며 재이용 활성화를 통해 최적의 물공급 방안이 마련되는 과정을 시각화함."
|
||||
width={650}
|
||||
height={500}
|
||||
className="h-[500px] mx-auto mb-4 rounded"
|
||||
/>
|
||||
<p className="font-NotoSansKR text-2xl font-bold text-gray-800 mt-10">(하수 재이용 시스템 개요도)</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,387 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { regionData, regionIdMap } from '@/app/common/comFunction';
|
||||
import { useDmStore } from '@/app/store';
|
||||
import type { DmStatusDataType } from '@/app/store/wrStatus/type';
|
||||
import { CHART_FORM, MainChart, type ChartConfig } from '@common/business_components';
|
||||
import React, { useEffect, useRef, useState, useCallback } from 'react';
|
||||
import { setCookie } from "../../../../src/shared/lib";
|
||||
|
||||
setCookie("site-id-key", process.env.NEXT_PUBLIC_SITE_ID || "DM");
|
||||
|
||||
interface WrStatusChartProps {
|
||||
chartForm: string;
|
||||
chartConfig: ChartConfig;
|
||||
chartData: any;
|
||||
chartTitle: string;
|
||||
chartClassName?: string;
|
||||
graphSize?: number;
|
||||
yFieldText? : string | undefined;
|
||||
yFieldText2? : string | undefined;
|
||||
showLegend? : boolean;
|
||||
setXAxisYearTick ?: boolean;
|
||||
}
|
||||
|
||||
export default function WrStatusPage() {
|
||||
|
||||
|
||||
|
||||
|
||||
const { Common, dmStatus, getDmStatusData,initializeCommonData} = useDmStore((state) => state);
|
||||
const { year:defaultYear, statusData,facilityTotals ,chartData} = dmStatus;
|
||||
const [selectedRegion, setSelectedRegion] = useState<string | null>(null);
|
||||
const [hoveredRegion, setHoveredRegion] = useState<string | null>(null);
|
||||
const [tooltipPosition, setTooltipPosition] = useState<{ x: number; y: number } | null>(null);
|
||||
const [chartOption, setChartOption] = useState<WrStatusChartProps>({} as WrStatusChartProps);
|
||||
|
||||
const svgContainerRef = useRef<HTMLDivElement>(null);
|
||||
const colorLegend = [
|
||||
{ label: '하수', color: '#FF6B6B', key: 'sewage' },
|
||||
{ label: '빗물', color: '#4ECDC4', key: 'rainwater' },
|
||||
{ label: '중수도', color: '#45B7D1', key: 'reclaimed' },
|
||||
];
|
||||
const facilityTotalsColor = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#FED16A'];
|
||||
// const [defaultYear, setDefaultYear] = useState<string>("");
|
||||
// const defaultYear= '2023';
|
||||
|
||||
|
||||
const handleMarkerClick = (regionId: string) => {
|
||||
if (selectedRegion === regionId) {
|
||||
setSelectedRegion(null);
|
||||
} else {
|
||||
setSelectedRegion(regionId);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
initializeCommonData();
|
||||
}, [initializeCommonData]);
|
||||
|
||||
const createDonutMarker = useCallback((svg: SVGSVGElement, x: number, y: number, name: string, values: number[]) => {
|
||||
const colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#FED16A'];
|
||||
const radius = 26;
|
||||
const innerRadius = 18;
|
||||
// const regionId = regionIdMap[name];
|
||||
const regionId = regionIdMap[name];
|
||||
if (!regionId) {
|
||||
console.warn(`Invalid region name: ${name}`);
|
||||
return;
|
||||
}
|
||||
const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
||||
g.setAttribute('transform', `translate(${x}, ${y})`);
|
||||
g.setAttribute('id', `marker-${name}`);
|
||||
g.style.cursor = 'pointer';
|
||||
g.setAttribute('role', 'button');
|
||||
g.setAttribute('tabindex', '0');
|
||||
g.setAttribute('aria-label', `${name} 지역 선택`);
|
||||
|
||||
const bg = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
|
||||
bg.setAttribute('r', radius.toString());
|
||||
bg.setAttribute('fill', 'white');
|
||||
bg.setAttribute('pointer-events', 'all');
|
||||
g.appendChild(bg);
|
||||
|
||||
let startAngle = 0;
|
||||
values.forEach((value, idx) => {
|
||||
const endAngle = startAngle + value * 2 * Math.PI;
|
||||
const x1 = radius * Math.sin(startAngle);
|
||||
const y1 = -radius * Math.cos(startAngle);
|
||||
const x2 = radius * Math.sin(endAngle);
|
||||
const y2 = -radius * Math.cos(endAngle);
|
||||
const largeArc = endAngle - startAngle > Math.PI ? 1 : 0;
|
||||
const d = `M ${x1} ${y1} A ${radius} ${radius} 0 ${largeArc} 1 ${x2} ${y2} L 0 0 Z`;
|
||||
|
||||
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
||||
path.setAttribute('d', d);
|
||||
path.setAttribute('fill', colors[idx % colors.length] as string);
|
||||
g.appendChild(path);
|
||||
|
||||
startAngle = endAngle;
|
||||
});
|
||||
|
||||
const center = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
|
||||
center.setAttribute('r', innerRadius.toString());
|
||||
center.setAttribute('fill', 'white');
|
||||
g.appendChild(center);
|
||||
|
||||
const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
||||
text.setAttribute('x', '0');
|
||||
text.setAttribute('y', '2');
|
||||
text.setAttribute('text-anchor', 'middle');
|
||||
text.setAttribute('font-size', '13');
|
||||
text.setAttribute('font-weight', 'bold');
|
||||
text.setAttribute('dominant-baseline', 'middle');
|
||||
text.setAttribute('fill', 'black');
|
||||
text.textContent = name || '';
|
||||
g.appendChild(text);
|
||||
|
||||
g.addEventListener('mouseenter', (e) => {
|
||||
setHoveredRegion(regionId);
|
||||
setTooltipPosition({ x: e.clientX, y: e.clientY });
|
||||
});
|
||||
|
||||
g.addEventListener('mouseleave', () => {
|
||||
setHoveredRegion(null);
|
||||
setTooltipPosition(null);
|
||||
});
|
||||
|
||||
g.addEventListener('click', () => {
|
||||
handleMarkerClick(regionId);
|
||||
});
|
||||
|
||||
g.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
|
||||
const matrix = g.getScreenCTM();
|
||||
if (!matrix) return; // 또는 console.warn('matrix is null'); return;
|
||||
|
||||
const point = svg.createSVGPoint();
|
||||
point.x = 0;
|
||||
point.y = 0;
|
||||
const transformed = point.matrixTransform(matrix);
|
||||
|
||||
setTooltipPosition({ x: transformed.x, y: transformed.y });
|
||||
setHoveredRegion(regionId);
|
||||
handleMarkerClick(regionId);
|
||||
}
|
||||
});
|
||||
|
||||
svg.appendChild(g);
|
||||
}, [handleMarkerClick, regionIdMap]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if(Common.selectSidoData&&Common.selectSidoData.length>1) {
|
||||
getDmStatusData(Common.selectSidoData);
|
||||
}
|
||||
},[Common.selectSidoData, getDmStatusData])
|
||||
|
||||
useEffect(() => {
|
||||
const loadSVG = async () => {
|
||||
try {
|
||||
const response = await fetch('/img/wrStatus/southKoreaMap.svg');
|
||||
const svgText = await response.text();
|
||||
|
||||
if (svgContainerRef.current) {
|
||||
svgContainerRef.current.innerHTML = svgText;
|
||||
|
||||
const svg = svgContainerRef.current.querySelector('svg') as SVGSVGElement;
|
||||
if (svg) {
|
||||
svg.style.width = '58%';
|
||||
svg.style.height = 'auto';
|
||||
svg.style.objectFit = 'contain';
|
||||
|
||||
// Set default fills
|
||||
const paths = svg.querySelectorAll('path');
|
||||
paths.forEach(path => {
|
||||
if (!path.getAttribute('fill') || path.getAttribute('fill') === 'none') {
|
||||
path.setAttribute('fill', '#ffffff');
|
||||
}
|
||||
});
|
||||
|
||||
// Create markers
|
||||
regionData.forEach(region => {
|
||||
createDonutMarker(svg, region.x, region.y, region.name, region.values);
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('SVG 로딩 실패:', error);
|
||||
if (svgContainerRef.current) {
|
||||
svgContainerRef.current.innerHTML = "<p className='text-red-500'>SVG 로딩 실패</p>";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
loadSVG();
|
||||
}, [createDonutMarker, regionData]);
|
||||
|
||||
useEffect(() => {
|
||||
const svg = svgContainerRef.current?.querySelector('svg');
|
||||
if (svg) {
|
||||
const paths = svg.querySelectorAll('path');
|
||||
paths.forEach(path => {
|
||||
const pathId = path.getAttribute('id');
|
||||
if (pathId) {
|
||||
if (selectedRegion === pathId) {
|
||||
path.setAttribute('fill', '#2563eb');
|
||||
} else {
|
||||
const original = '#ffffff';
|
||||
path.setAttribute('fill', original);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [selectedRegion]);
|
||||
useEffect(() => {
|
||||
if(chartData.length>0){
|
||||
const chartConfig = chartData.reduce((config, element) => {
|
||||
config[element.label] = {
|
||||
label: element.label,
|
||||
color : "hsl(var(--chart-1))"
|
||||
};
|
||||
return config;
|
||||
}, {} as Record<string, { label: string; color: string; }>) satisfies ChartConfig;
|
||||
|
||||
const Option = {
|
||||
chartForm : CHART_FORM.BAR,
|
||||
chartConfig : chartConfig,
|
||||
chartData : chartData,
|
||||
chartTitle : '',
|
||||
chartClassName : "w-full h-[350px] ",
|
||||
graphSize : 10,
|
||||
yFieldText : '시설(개소)',
|
||||
showLegend : true
|
||||
}
|
||||
setChartOption(Option)
|
||||
}else{
|
||||
setChartOption({} as WrStatusChartProps)
|
||||
}
|
||||
}, [chartData]);
|
||||
|
||||
return (
|
||||
<div className="flex h-screen bg-[#F2F2F2] text-gray-800">
|
||||
{/* 우측 콘텐츠 영역 */}
|
||||
<main className="flex-1 p-6 overflow-auto">
|
||||
{/* 상단 제목 */}
|
||||
<div className="mb-6 flex justify-between items-center">
|
||||
<h2 className="text-2xl font-bold">물재이용 현황</h2>
|
||||
</div>
|
||||
|
||||
{/* 콘텐츠 본문 */}
|
||||
<div className="grid grid-cols-1 xl:grid-cols-2 gap-4">
|
||||
{/* 지도 카드 */}
|
||||
<div className="bg-white rounded-xl shadow border border-[#DBE2E6] overflow-hidden max-h-[900px]">
|
||||
{/* 타이틀 영역 */}
|
||||
<div className="border-b border-[#DBE2E6] px-4 py-3">
|
||||
<h3 className="text-[12px] font-semibold">{`재이용시설현황(${defaultYear})`}</h3>
|
||||
<p className="text-[12px] text-[#999] font-medium">행정구역별 재이용시설현황(개소)</p>
|
||||
</div>
|
||||
|
||||
{/* 본문 콘텐츠 */}
|
||||
<div className="p-4">
|
||||
<div className="bg-[#E2EDF3] p-4 rounded shadow relative">
|
||||
<div className="flex justify-center">
|
||||
<div
|
||||
ref={svgContainerRef}
|
||||
className="w-full max-w-5xl max-h-[680px] overflow-y-auto block mx-auto"
|
||||
aria-label="대한민국 지역별 재이용시설 분포도"
|
||||
role="region"
|
||||
tabIndex={0}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{typeof window !== 'undefined' && hoveredRegion && tooltipPosition && (
|
||||
<div
|
||||
className="fixed bg-white text-[12px] text-gray-700 border border-gray-300 shadow-lg rounded px-3 py-2 z-50"
|
||||
style={{
|
||||
top: tooltipPosition.y + 10,
|
||||
left: tooltipPosition.x + 10,
|
||||
pointerEvents: 'none',
|
||||
}}
|
||||
>
|
||||
<div className="font-semibold mb-1">{hoveredRegion}</div>
|
||||
<div className="space-y-1">
|
||||
{colorLegend.map(({ label, color, key }) => (
|
||||
<div key={key+'' +color} className="flex items-center space-x-2">
|
||||
<span className="text-[12px] inline-block w-3 h-3 rounded-sm" style={{ backgroundColor: color }}></span>
|
||||
<span className="text-[12px]">
|
||||
{label}:{' '}
|
||||
{statusData?.find(d => d.region === hoveredRegion)?.[key as keyof DmStatusDataType] || '-'}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 범례 */}
|
||||
<div className="absolute bottom-2 right-2 bg-white rounded-lg shadow-md p-4 text-sm space-y-2">
|
||||
{facilityTotals?.map((item, index) => (
|
||||
<div key ={'fcltTotal' +index}className="flex items-center space-x-2">
|
||||
<span className="inline-block w-4 h-4 rounded-sm" style={{backgroundColor:facilityTotalsColor[index]}}></span>
|
||||
<span className="text-[12px] text-gray-900">{item}</span>
|
||||
</div>
|
||||
)) }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 우측 콘텐츠 (표 + 차트) */}
|
||||
<div className="col-span-1 flex flex-col space-y-4">
|
||||
{/* 재이용시설현황 표 */}
|
||||
<div className="bg-white rounded-xl shadow border border-[#DBE2E6] overflow-hidden">
|
||||
{/* 타이틀 + 버튼 영역 */}
|
||||
<div className="flex justify-between items-center border-b border-[#436d85] px-4 py-3">
|
||||
<div>
|
||||
<h3 className="text-[12px] font-semibold">{`재이용시설현황(${defaultYear})`}</h3>
|
||||
<p className="text-[12px] text-[#999] font-medium">행정구역별 재이용시설현황[개소(천m³/년)]</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-4">
|
||||
<div className="rounded border border-[#DBE2E6] shadow overflow-auto max-h-[292px] p-4">
|
||||
<table className="min-w-full text-sm text-center">
|
||||
<caption className="sr-only text-[12px]">행정구역별 하수, 빗물, 중수도, 온배수 재이용시설 수</caption>
|
||||
<thead className="bg-gray-50 text-xs text-gray-600 uppercase border-b">
|
||||
<tr>
|
||||
<th className="text-[12px] font-semibold px-4 py-2">번호</th>
|
||||
<th className="text-[12px] font-semibold px-4 py-2">시도</th>
|
||||
<th className="text-[12px] font-semibold px-4 py-2">하수재이용시설</th>
|
||||
<th className="text-[12px] font-semibold px-4 py-2">빗물이용시설</th>
|
||||
<th className="text-[12px] font-semibold px-4 py-2">중수도시설</th>
|
||||
{/* <th className="text-[12px] font-semibold px-4 py-2">하수(이용량,천𝓂³/년)</th>
|
||||
<th className="text-[12px] font-semibold px-4 py-2">빗물(이용량,천𝓂³/년)</th>
|
||||
<th className="text-[12px] font-semibold px-4 py-2">중수도(이용량,천𝓂³/년)</th>
|
||||
<th className="text-[12px] font-semibold px-4 py-2">온배수(이용량,천𝓂³/년)</th> */}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y">
|
||||
{statusData?.map((row, index) => (
|
||||
<tr key={index} className="hover:bg-gray-50">
|
||||
<td className="text-[12px] px-4 py-2 text-center">
|
||||
{index+1}
|
||||
</td>
|
||||
<td className="text-[12px]">{row.region}</td>
|
||||
<td className="text-[12px]">{row.sewage}</td>
|
||||
<td className="text-[12px]">{row.rainwater}</td>
|
||||
<td className="text-[12px]">{row.reclaimed}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 재이용통계 차트 */}
|
||||
<div className="bg-white rounded-xl shadow border border-[#DBE2E6] overflow-hidden">
|
||||
<div className="border-b border-[#DBE2E6] px-4 py-3">
|
||||
<h3 className="text-[12px] font-semibold">{`재이용통계(${defaultYear})`}</h3>
|
||||
<p className="text-[12px] text-[#999] font-medium">행정구역별 재이용시설 현황 개소수 그래프</p>
|
||||
</div>
|
||||
<div className="p-4 bg-[#f9fbfd]">
|
||||
<div className="h-[305px] text-gray-500">
|
||||
{chartOption?
|
||||
<MainChart
|
||||
chartForm={chartOption.chartForm}
|
||||
chartConfig={chartOption.chartConfig}
|
||||
chartData={chartOption.chartData?.filter(item=>item.label!= '')}
|
||||
chartTitle={chartOption.chartTitle}
|
||||
chartClassName={chartOption.chartClassName}
|
||||
graphSize={chartOption.graphSize}
|
||||
yFieldText={chartOption.yFieldText}
|
||||
showLegend={chartOption.showLegend}
|
||||
/>
|
||||
:<></>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -29,10 +29,15 @@ const Map = () => {
|
||||
ruseUtztnFcltCd: fclt.pbnstRuseFcltId,
|
||||
}));
|
||||
|
||||
if (!window.opener || typeof window.opener.postMessage !== "function") {
|
||||
alert("부모 창을 찾을 수 없습니다. 팝업이 'noopener'로 열렸거나, 보안 정책으로 차단되었을 수 있어요.");
|
||||
return;
|
||||
}
|
||||
|
||||
window.opener.postMessage(
|
||||
{
|
||||
type: "SET_SUBS_INFO",
|
||||
payload: subsInfoList, // 또는 필요한 데이터
|
||||
payload: subsInfoList, // 배열로 전송 (최대 3개)
|
||||
},
|
||||
"*"
|
||||
);
|
||||
@@ -52,9 +57,23 @@ const Map = () => {
|
||||
|
||||
//하수처리시설코드로 재이용시설 목록 조회
|
||||
const reuseData = async (ruseFcltPk: any) => {
|
||||
if (!ruseFcltPk) {
|
||||
console.warn("[reuseData] ruseFcltPk가 없습니다.");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
console.log("[reuseData] API 호출 - ruseFcltPk:", ruseFcltPk);
|
||||
const response = await mapService.getPbnstRuseFclts(ruseFcltPk);
|
||||
const data = response.data || [];
|
||||
console.log("[reuseData] API 응답 전체:", response);
|
||||
// 응답이 배열이면 바로 사용, 객체면 .data 추출
|
||||
const data = Array.isArray(response)
|
||||
? response
|
||||
: Array.isArray(response?.data)
|
||||
? response.data
|
||||
: response?.data
|
||||
? [response.data]
|
||||
: [];
|
||||
console.log("[reuseData] 파싱된 data:", data);
|
||||
setSelectedFclts((prev) => {
|
||||
const newItems = data.filter(
|
||||
(item: any) =>
|
||||
@@ -62,7 +81,7 @@ const Map = () => {
|
||||
Object.keys(item).length > 0 &&
|
||||
!prev.some((f) => f.pbnstRuseFcltId === item.pbnstRuseFcltId)
|
||||
);
|
||||
console.log(newItems);
|
||||
console.log("[reuseData] 추가될 newItems:", newItems);
|
||||
return [...prev, ...newItems];
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -75,11 +94,45 @@ const Map = () => {
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
// 부모창에 초기 선택값 요청 (팝업 열릴 때 기존 선택 목록 받아오기)
|
||||
useEffect(() => {
|
||||
const origin = window.location.origin;
|
||||
if (window.opener) {
|
||||
window.opener.postMessage({ type: "REQUEST_INIT_SUBS_INFO" }, origin);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// 부모창에서 초기 선택값 수신
|
||||
useEffect(() => {
|
||||
const origin = window.location.origin;
|
||||
|
||||
const handler = (event: MessageEvent) => {
|
||||
if (event.origin !== origin) return;
|
||||
|
||||
const data = event.data;
|
||||
if (!data || typeof data !== "object") return;
|
||||
|
||||
if (data.type === "INIT_SUBS_INFO") {
|
||||
const list = Array.isArray(data.payload) ? data.payload : [];
|
||||
setSelectedFclts(
|
||||
list.map((x) => ({
|
||||
ruseUtztnFcltNm: x.ruseUtztnFcltNm ?? "",
|
||||
pbnstRuseFcltId: x.ruseUtztnFcltCd ?? "",
|
||||
}))
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("message", handler);
|
||||
return () => window.removeEventListener("message", handler);
|
||||
}, []);
|
||||
|
||||
// 레이어 생성 후 초기화 처리
|
||||
const onCheckedLayersChange = () => {
|
||||
//수요처 ID
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const dmdInfoId = urlParams.get("dmdInfoId");
|
||||
console.log("[하이라이트] dmdInfoId:", dmdInfoId);
|
||||
|
||||
//수요처 위치로 화면 이동
|
||||
const maps = gisMaps.current;
|
||||
@@ -88,22 +141,42 @@ const Map = () => {
|
||||
const featureId = `${layerNm}.${dmdInfoId}`;
|
||||
let foundFeature: any = null;
|
||||
|
||||
// 현재 로드된 레이어 목록 출력
|
||||
const allLayerNames = maps
|
||||
.getLayers()
|
||||
.getArray()
|
||||
.map((l: any) => l.get("lyrTyNm") || l.get("name") || "(unnamed)");
|
||||
console.log("[하이라이트] 전체 레이어 목록:", allLayerNames);
|
||||
console.log("[하이라이트] 탐색 대상 레이어:", layerNm);
|
||||
console.log("[하이라이트] 탐색 대상 featureId:", featureId);
|
||||
|
||||
maps
|
||||
.getLayers()
|
||||
.getArray()
|
||||
.forEach((layer: any) => {
|
||||
if (layer.get("lyrTyNm") == layerNm) {
|
||||
console.log("[하이라이트] 레이어 발견:", layerNm);
|
||||
const source = layer.getSource?.();
|
||||
if (source?.getFeatureById) {
|
||||
const feature = source.getFeatureById(String(featureId).trim());
|
||||
console.log("[하이라이트] feature 탐색결과:", feature);
|
||||
if (feature) {
|
||||
foundFeature = feature;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// feature 못 찾을 때 소스 내 전체 feature ID 출력
|
||||
if (source?.getFeatures) {
|
||||
const allIds = source.getFeatures().map((f: any) => f.getId());
|
||||
console.log("[하이라이트] 소스 내 전체 feature ID:", allIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!foundFeature) {
|
||||
console.warn("[하이라이트] feature를 찾지 못했습니다. 위 레이어 목록/ID를 확인하세요.");
|
||||
}
|
||||
|
||||
if (foundFeature) {
|
||||
const geometry = foundFeature.getGeometry();
|
||||
|
||||
@@ -133,7 +206,7 @@ const Map = () => {
|
||||
{ width: 30 }
|
||||
);
|
||||
} else {
|
||||
alert("수요처의 좌표정보를 찾을수 없으므로, 상단 조회조건의 시설명을 입력하여 위치를 조회하시기 바랍니다.");
|
||||
console.warn("수요처의 좌표정보를 찾을 수 없습니다. dmdInfoId:", dmdInfoId);
|
||||
}
|
||||
|
||||
const formatNumber = (val: any) => {
|
||||
@@ -185,6 +258,7 @@ const Map = () => {
|
||||
|
||||
// 검색해서 재이용시설 선택하면 해당 시설로 화면 이동 처리
|
||||
useEffect(() => {
|
||||
console.warn(selectDataPk);
|
||||
if (!selectDataPk || Object.keys(selectDataPk).length === 0) return;
|
||||
const maps = gisMaps.current;
|
||||
let foundFeature: any = null;
|
||||
@@ -226,8 +300,12 @@ const Map = () => {
|
||||
|
||||
// 재이용시설 클릭 시 선택 시설목록에 추가
|
||||
const onFeatureSelect = (data: any) => {
|
||||
// feature의 모든 속성 출력 (어떤 키가 있는지 확인)
|
||||
const properties = data.getProperties?.() ?? {};
|
||||
console.log("[onFeatureSelect] feature 속성 전체:", properties);
|
||||
// 처리시설코드
|
||||
const swtrmtFcltCd = data.get("swtrmt_fclt_cd");
|
||||
console.log("[onFeatureSelect] swtrmt_fclt_cd:", swtrmtFcltCd);
|
||||
reuseData(swtrmtFcltCd);
|
||||
};
|
||||
|
||||
|
||||
@@ -22,36 +22,32 @@ export default function SelectedFcltList({
|
||||
|
||||
const TableContent = (
|
||||
<>
|
||||
<thead className="bg-gray-100 text-gray-700 text-[13px]">
|
||||
<thead className="bg-gray-100 text-gray-600 text-[11px] font-medium border-b border-cyan-800">
|
||||
<tr>
|
||||
<th className="px-3 py-2 text-left border-b">시설명</th>
|
||||
<th className="px-3 py-2 text-right border-b">
|
||||
하수처리량
|
||||
<br />
|
||||
(㎥/일)
|
||||
<th className="px-2 py-1.5 text-left w-1/3">시설명</th>
|
||||
<th className="px-2 py-1.5 text-right whitespace-nowrap">
|
||||
처리량(㎥/일)
|
||||
</th>
|
||||
<th className="px-3 py-2 text-right border-b">
|
||||
재이용량
|
||||
<br />
|
||||
(㎥/일)
|
||||
<th className="px-2 py-1.5 text-right whitespace-nowrap">
|
||||
재이용량(㎥/일)
|
||||
</th>
|
||||
<th className="px-3 py-2 border-b">삭제</th>
|
||||
<th className="px-2 py-1.5 text-center w-10">삭제</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{data.map((item) => (
|
||||
<tr key={item.pbnstRuseFcltId} className="border-b last:border-none">
|
||||
<td className="px-3 py-2 text-[12px]">{item.ruseUtztnFcltNm}</td>
|
||||
<td className="px-3 py-2 text-right text-[12px]">
|
||||
<tr key={item.pbnstRuseFcltId} className="border-b border-gray-100 last:border-none hover:bg-gray-50/50">
|
||||
<td className="px-2 py-1.5 text-[11px] font-medium text-gray-800 break-keep">{item.ruseUtztnFcltNm}</td>
|
||||
<td className="px-2 py-1.5 text-right text-[11px] text-gray-600">
|
||||
{Number(item.splyExtraCpct || 0).toLocaleString()}
|
||||
</td>
|
||||
<td className="px-3 py-2 text-right text-[12px]">
|
||||
<td className="px-2 py-1.5 text-right text-[11px] text-gray-600">
|
||||
{Number(item.ruseCpct || 0).toLocaleString()}
|
||||
</td>
|
||||
<td className="px-3 py-2 text-center">
|
||||
<td className="px-2 py-1.5 text-center">
|
||||
<button
|
||||
onClick={() => handleDelete(item.pbnstRuseFcltId)}
|
||||
className="text-red-500 hover:text-red-700 text-sm font-bold"
|
||||
className="text-gray-400 hover:text-red-500 text-[11px] font-bold transition-colors"
|
||||
aria-label="삭제"
|
||||
title="삭제"
|
||||
>
|
||||
@@ -64,15 +60,13 @@ export default function SelectedFcltList({
|
||||
</>
|
||||
);
|
||||
|
||||
if (data.length === 0) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed right-40 top-[8rem] w-[40rem] max-h-[130rem] overflow-auto bg-white shadow-lg rounded p-4 border border-gray-300 z-50">
|
||||
<h3 className="bg-cyan-700 text-white text-xl font-bold mb-2 p-2">
|
||||
<div className="fixed right-20 top-[3.5rem] w-[22rem] max-h-[80vh] overflow-hidden bg-white/95 backdrop-blur-sm shadow-[0_4px_12px_rgba(0,0,0,0.1)] rounded-lg border border-gray-200 z-50 flex flex-col">
|
||||
<h3 className="bg-cyan-700 text-white text-[13px] font-semibold p-2">
|
||||
선택된 시설
|
||||
</h3>
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-sm text-gray-800 border border-gray-200">
|
||||
<div className="overflow-y-auto w-full">
|
||||
<table className="w-full text-sm text-gray-800 border-t border-gray-200 table-fixed">
|
||||
{TableContent}
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,7 @@ import { DEV_URL_WR, DEV_URL_SN } from "@/components/ui/config";
|
||||
export type LyrStngProps = {
|
||||
lyr_sn: string
|
||||
lyr_nm: string
|
||||
upper_lyr_sn: string
|
||||
upper_lyr_sn: string
|
||||
drawOrdr: string
|
||||
flterSql: string
|
||||
gobjTy: string
|
||||
@@ -29,7 +29,7 @@ export type LyrStngProps = {
|
||||
export const mapService = {
|
||||
//레이어 설정 조회
|
||||
getLyrStngs: async () => {
|
||||
const response = await fetch(`${DEV_URL_SN}/api/v1/map/lyrStngs/DM_NETWORK`, {
|
||||
const response = await fetch(`${DEV_URL_SN}/map/lyrStngs/DM_NETWORK`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
@@ -42,7 +42,7 @@ export const mapService = {
|
||||
|
||||
return response.json();
|
||||
},
|
||||
getPbnstRuseFclts: async (ruseFcltPk:any) => {
|
||||
getPbnstRuseFclts: async (ruseFcltPk: any) => {
|
||||
const response = await fetch(`${DEV_URL_WR}/reuseFc/pubFc/${ruseFcltPk}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { BaseLayout } from "@common/components";
|
||||
import type { MenuItem } from "./store/common";
|
||||
import { useDmStore } from "./store";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
|
||||
export default function Base({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
const { getMenuAuth } = useDmStore((state) => state);
|
||||
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
|
||||
const handleClick = (id: MenuItem) => {
|
||||
getMenuAuth(id.menuCd);
|
||||
router.push(id.urlPath);
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseLayout currentPath={pathname ?? ""} handleMenuClick={handleClick}>
|
||||
{children}
|
||||
</BaseLayout>
|
||||
);
|
||||
}
|
||||
+14
-1
@@ -1,4 +1,5 @@
|
||||
@import "../../../packages/common/components/src/base.css";
|
||||
/* @import "../../../packages/common/components/src/base.css"; */
|
||||
/* @import "../../../packages/common/components/src/base.css"; */
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@@ -68,3 +69,15 @@
|
||||
@apply bg-background text-foreground;
|
||||
}
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "NotoSansKR";
|
||||
src: url("/fonts/NotoSansKR-Regular.woff2") format("woff2");
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "NotoSansKR", sans-serif;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,11 @@ export default function RootLayout({
|
||||
<head>
|
||||
<title>물재이용 대민</title>
|
||||
</head>
|
||||
<body>{children}</body>
|
||||
<body>
|
||||
<div className="min-w-[1800px] overflow-x-auto">
|
||||
{children}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user