diff --git a/.gitignore b/.gitignore index 878447ead..ab07be55d 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ apps/fo/.next/**/* pnpm-lock.yaml settings.json .npmrc +.cursor \ No newline at end of file diff --git a/apps/cm/.env.development b/apps/cm/.env.development deleted file mode 100644 index 9f786831b..000000000 --- a/apps/cm/.env.development +++ /dev/null @@ -1,7 +0,0 @@ -# apps/cs/src/shared/config/env.ts 참고 -NEXT_PUBLIC_PORT=3000 -NEXT_PUBLIC_PROXY_HOST=http://localhost:3000 -NEXT_PUBLIC_SITE_ID=CS -NEXT_PUBLIC_SUB_DOMAIN=http://localhost:3000 -NEXT_PUBLIC_SERVER_API_URL=http://192.168.0.102:8000 -NEXT_PUBLIC_TINY_API_KEY=t9nc4z6inquvvd5pq1vea8wcp7lb01qmqseh6egms9vpaju2 \ No newline at end of file diff --git a/apps/cm/.env.example b/apps/cm/.env.example index 9f786831b..5d9747b57 100644 --- a/apps/cm/.env.example +++ b/apps/cm/.env.example @@ -1,7 +1,14 @@ # apps/cs/src/shared/config/env.ts 참고 -NEXT_PUBLIC_PORT=3000 -NEXT_PUBLIC_PROXY_HOST=http://localhost:3000 -NEXT_PUBLIC_SITE_ID=CS -NEXT_PUBLIC_SUB_DOMAIN=http://localhost:3000 -NEXT_PUBLIC_SERVER_API_URL=http://192.168.0.102:8000 -NEXT_PUBLIC_TINY_API_KEY=t9nc4z6inquvvd5pq1vea8wcp7lb01qmqseh6egms9vpaju2 \ No newline at end of file +NEXT_PUBLIC_PORT=3001 +NEXT_PUBLIC_PROXY_HOST=http://localhost:3001 +NEXT_PUBLIC_SITE_ID=CM +NEXT_PUBLIC_SUB_DOMAIN=http://localhost:13000 +# NEXT_PUBLIC_SERVER_API_URL=http://localhost:8000 +NEXT_PUBLIC_SERVER_API_URL=http://dev.onasys.co.kr:8000 +NEXT_PUBLIC_LINK_BACKEND_API_HOST=http://rsone.iptime.org:10014 +NEXT_PUBLIC_TINY_API_KEY=t9nc4z6inquvvd5pq1vea8wcp7lb01qmqseh6egms9vpaju2 + +NEXT_PUBLIC_SERVER_API_URL2=http://con.mind-one.co.kr:11083 +NEXT_PUBLIC_SERVER_API_URL3=https://apigateway.apps.rsonesoft.com +NEXT_PUBLIC_SN=https://devlion.hasuon.co.kr +NEXT_PUBLIC_WR=https://devtiger.hasuon.co.kr diff --git a/apps/cm/.env.local b/apps/cm/.env.local deleted file mode 100644 index d0187a5e8..000000000 --- a/apps/cm/.env.local +++ /dev/null @@ -1,14 +0,0 @@ -# apps/cs/src/shared/config/env.ts 참고 -NEXT_PUBLIC_PORT=3000 -NEXT_PUBLIC_PROXY_HOST=http://localhost:3000 -NEXT_PUBLIC_SITE_ID=CM -NEXT_PUBLIC_SUB_DOMAIN=http://localhost:13000 -# NEXT_PUBLIC_SERVER_API_URL=http://localhost:8000 -NEXT_PUBLIC_SERVER_API_URL=http://dev.onasys.co.kr:8000 -NEXT_PUBLIC_LINK_BACKEND_API_HOST=http://rsone.iptime.org:10014 -NEXT_PUBLIC_TINY_API_KEY=t9nc4z6inquvvd5pq1vea8wcp7lb01qmqseh6egms9vpaju2 - -NEXT_PUBLIC_SERVER_API_URL2=http://con.mind-one.co.kr:11083 -NEXT_PUBLIC_SERVER_API_URL3=https://apigateway.apps.rsonesoft.com -NEXT_PUBLIC_SN=https://devlion.hasuon.co.kr -NEXT_PUBLIC_WR=https://devtiger.hasuon.co.kr diff --git a/apps/cm/.env.production b/apps/cm/.env.production deleted file mode 100644 index 750b4022e..000000000 --- a/apps/cm/.env.production +++ /dev/null @@ -1,11 +0,0 @@ -# apps/cs/src/shared/config/env.ts 참고 -NEXT_PUBLIC_PORT=3000 -NEXT_PUBLIC_PROXY_HOST=http://localhost:3000 -NEXT_PUBLIC_SITE_ID=CS -NEXT_PUBLIC_SUB_DOMAIN=http://localhost:13000 -NEXT_PUBLIC_SERVER_API_URL=http://192.168.0.102:8000 -NEXT_PUBLIC_TINY_API_KEY=t9nc4z6inquvvd5pq1vea8wcp7lb01qmqseh6egms9vpaju2 - -NEXT_PUBLIC_SERVER_API_URL2=http://apigateway.iipbs-was.svc.cluster.local:8000 -NEXT_PUBLIC_SN=https://lion.hasuon.co.kr -NEXT_PUBLIC_WR=https://tiger.hasuon.co.kr \ No newline at end of file diff --git a/apps/cm/.env.zip b/apps/cm/.env.zip deleted file mode 100644 index 610032d09..000000000 Binary files a/apps/cm/.env.zip and /dev/null differ diff --git a/apps/cm/app/api/auth/logout/route.ts b/apps/cm/app/api/auth/logout/route.ts index ba8e23374..fdcc9df7c 100644 --- a/apps/cm/app/api/auth/logout/route.ts +++ b/apps/cm/app/api/auth/logout/route.ts @@ -15,4 +15,3 @@ export async function POST() { cookieStore.delete(CLAIM_NAME); return NextResponse.json({ message: "Logged out" }, { status: 200 }); } - diff --git a/apps/cm/next.config.ts b/apps/cm/next.config.ts index 679528324..b388b76d9 100644 --- a/apps/cm/next.config.ts +++ b/apps/cm/next.config.ts @@ -3,10 +3,22 @@ import type { NextConfig } from "next"; const subDomain = process.env.NEXT_PUBLIC_SUB_DOMAIN || "http://localhost:13000"; -const backendUrl = process.env.NEXT_PUBLIC_SERVER_API_URL || "http://192.168.0.102:8000"; - const nextConfig: NextConfig = { output: "standalone", + experimental: { + proxyTimeout: 600000, + }, + eslint: { + ignoreDuringBuilds: true, + }, + typescript: { + ignoreBuildErrors: true, + }, + logging: { + fetches: { + fullUrl: true, + }, + }, async rewrites() { return { beforeFiles: [ @@ -27,10 +39,6 @@ const nextConfig: NextConfig = { source: "/cms/:path*", destination: `${subDomain}/cms/:path*`, }, - { - source: "/user-service/:path*", - destination: `${backendUrl}/user-service/:path*`, - }, ], fallback: [], }; diff --git a/apps/cm/src/entities/auth/lib/token-manager.ts b/apps/cm/src/entities/auth/lib/token-manager.ts index 21ed00218..8fcf21984 100644 --- a/apps/cm/src/entities/auth/lib/token-manager.ts +++ b/apps/cm/src/entities/auth/lib/token-manager.ts @@ -1,4 +1,4 @@ -import { AUTH_USER_ID, CLAIM_NAME } from "@/shared/config"; +import { ACCESS_TOKEN, AUTH_USER_ID, CLAIM_NAME, REFRESH_TOKEN } from "@/shared/config"; import { getCookie, setCookie, removeCookie } from "@/shared/lib"; export class TokenManager { @@ -76,6 +76,8 @@ export class TokenManager { this.removeToken(CLAIM_NAME); this.removeToken(AUTH_USER_ID); + this.removeToken(REFRESH_TOKEN); + this.removeToken(ACCESS_TOKEN); } } diff --git a/apps/cm/src/pages/dashboard/ui/mypost-list.tsx b/apps/cm/src/pages/dashboard/ui/mypost-list.tsx index ec5f97d48..2c15b7d7d 100644 --- a/apps/cm/src/pages/dashboard/ui/mypost-list.tsx +++ b/apps/cm/src/pages/dashboard/ui/mypost-list.tsx @@ -3,7 +3,7 @@ import { useRouter } from "next/navigation"; import { useQuery } from "@tanstack/react-query"; import { getCoreRowModel } from "@tanstack/react-table"; -import { PostApi, type MyPost } from "@/entities/post"; +import { PostApi, type MyPost, type NoticePost } from "@/entities/post"; import { ENDPOINT } from "@/shared/config"; import { useDataTable } from "@/shared/lib"; import { @@ -23,15 +23,21 @@ const MyPostList: React.FC = () => { }), }); + const handleMyPostRowClick = (row: MyPost) => { + router.push(`/cms/${row.bbsTypeCd.toLowerCase()}/${row.bbsId}/${row.pstNo}`); + }; + const { tableComponent: myPostTableComponent } = useDataTable({ query: myPostQuery, tableOptions: { - columns: myPostTableColumns, - getCoreRowModel: getCoreRowModel(), + columns: myPostTableColumns, + getCoreRowModel: getCoreRowModel(), }, dataTableOptions: { - variant: "line", + variant: "line", + onRowClick: handleMyPostRowClick }, + }); return ( diff --git a/apps/cm/src/pages/dashboard/ui/notice-list.tsx b/apps/cm/src/pages/dashboard/ui/notice-list.tsx index 86e21a4a3..502973225 100644 --- a/apps/cm/src/pages/dashboard/ui/notice-list.tsx +++ b/apps/cm/src/pages/dashboard/ui/notice-list.tsx @@ -26,7 +26,7 @@ const NoticeList: React.FC = () => { }); const handleNoticePostRowClick = (row: NoticePost) => { - router.push(`/bbs/bbsnotice/${row.bbsId}/${row.pstNo}`); + router.push(`/cms/${row.bbsTypeCd.toLowerCase()}/${row.bbsId}/${row.pstNo}`); }; const handleMoreClick = () => { @@ -40,12 +40,12 @@ const NoticeList: React.FC = () => { const { tableComponent: noticePostTableComponent } = useDataTable({ query: noticePostQuery, tableOptions: { - columns: noticePostTableColumns, - getCoreRowModel: getCoreRowModel(), + columns: noticePostTableColumns, + getCoreRowModel: getCoreRowModel(), }, dataTableOptions: { - variant: "line", - onRowClick: handleNoticePostRowClick, + variant: "line", + onRowClick: handleNoticePostRowClick, }, }); diff --git a/apps/cs/app/api/proxy/[...url]/route.ts b/apps/cs/app/api/proxy/[...url]/route.ts index 7442c702f..abd3ae007 100644 --- a/apps/cs/app/api/proxy/[...url]/route.ts +++ b/apps/cs/app/api/proxy/[...url]/route.ts @@ -60,13 +60,18 @@ async function handler( [AUTH_USER_ID]: userId, }); + const forwardedHost = request.headers.get("x-forwarded-host"); + const host = request.headers.get("host"); + const actualHost = forwardedHost || host || new URL(request.url).hostname.toString(); + // 운영 환경에서 domain 설정 + const domain = actualHost.includes("hasuon.or.kr") ? ".hasuon.or.kr" : actualHost; if (refreshToken) { cookieStore.set(REFRESH_TOKEN, refreshToken, { httpOnly: true, - sameSite: "lax", -// domain: domain, + sameSite: "strict", + domain: domain, path: "/", }); } diff --git a/apps/cs/docs/표준단어정의서.md b/apps/cs/docs/표준단어정의서.md new file mode 100644 index 000000000..6950eb84b --- /dev/null +++ b/apps/cs/docs/표준단어정의서.md @@ -0,0 +1,831 @@ +# 표준단어정의서 + +AI 이해용으로 정리된 표준단어명, 영문약어명, 영문명 목록입니다. + +| 표준단어명 | 표준단어 영문약어명 | 표준단어 영문명 | +|-----------|-------------------|----------------| +| BOD | BOD | Biochemical Oxygen Demand | +| C/N비 | CNR | Carbon-Nitrogen Ratio | +| COD | COD | Chemical Oxygen Demand | +| F/M비 | FMR | Food-Microorganism Ratio | +| GISD | GISDATA | Geographic Information System Data | +| HTML | HTML | Hypertext Markup Language | +| ID | ID | Id | +| IP | IP | Internet Protocol | +| KEY | KEY | Key | +| ND | ND | Not Detected | +| NON | NON | Non | +| NTU | NTU | Nephelometry Turbidity Unit | +| OPT | OPT | Opt | +| PAC | PAC | Polyaluminium Chloride | +| PH | PH | Hydrogen Exponent | +| PKI | PKI | Public Key Infrastructure | +| POOL | POOL | Pool | +| SS | SS | Suspended Solids (Ss) | +| TCE | TCE | Trichloroethane | +| TM | TM | Transverse Mercator | +| T-N | TN | Total Nitrogen | +| TOC | TOC | Total Organic Carbon | +| T-P | TP | Total Phosphorus | +| URL | URL | Uniform Resource Locator | +| 가구 | HSHD | Household | +| 가능 | PSBL | Possible | +| 가동 | OPR | Operation | +| 가산 | ADTN | Addition | +| 가성소다 | NAOH | Caustic Soda(Nach) | +| 가축분뇨 | ANMA | Animal Manure | +| 값 | VAL | Value | +| 강도 | IN | Intensity | +| 강우 | RAIN | Rain | +| 개방 | OPN | Opening | +| 개별 | INDIV | Individual | +| 개보수비 | RPAFEE | Repair Fee | +| 개설 | ESTBL | Establishment | +| 개시 | STRT | Start | +| 개정 | RVSN | Revision | +| 개조 | REMDEL | Remodeling | +| 개체 | INDVD | Individual | +| 개체군 | POPU | Population | +| 개폐 | RORGN | Reorganization | +| 갱신 | UPDT | Update | +| 건물 | BLDG | Building | +| 건설 | CONC | Construction | +| 건수 | NOCS | The Number Of Cases | +| 건조 | DRY | Dry | +| 검사 | INSP | Inspection | +| 검사서 | EXRPT | Examination Report | +| 검정 | RCGN | Recognition | +| 검증 | VRFC | Verification | +| 검출 | DETECT | Detection | +| 검토 | RVW | Review | +| 격자 | LATTC | Lattice | +| 결과 | RSLT | Result | +| 결의 | DCIS | Decision | +| 결정 | DCSN | Decision | +| 경계 | BNDRY | Boundary | +| 경과 | PROC | Process | +| 경도 | LOT | Longitude | +| 경보 | ALARM | Alarm | +| 경사판 | ICNTPT | Inclination plate | +| 계 | SUM | Summation | +| 계량기 | MRNR | Measuring Instrument | +| 계량기분리 | METRSPRT | Meter Separation | +| 계면 | SGLV | Sludge Level | +| 계산식 | FMUL | Formula | +| 계약 | CTRT | Contract | +| 계약기간 | CNTRPD | Contract Period | +| 계절 | SEASON | Season | +| 계획 | PLAN | Plan | +| 고도처리 | AWTR | Advanced Water Treatment | +| 고시 | ANCMNT | Announcement | +| 고액분리 | SLSPRT | Solid-Liquid Separation | +| 고형물체류시간 | SRT | Solid Retention Time | +| 공간데이터 | SPCEDATA | Space Data | +| 공공 | PUBLIC | Public | +| 공급 | SPLY | Supply | +| 공기 | AIR | Air | +| 공기여과 | ARFT | Air Filter | +| 공문 | ODOC | Official Documnet | +| 공법 | MSRC | Method Of Construction | +| 공사 | CSTRN | Construction | +| 공사비 | CONSTEXPN | Construction Expenses | +| 공업지역 | MNFTR_ARA | Manufacturing Area | +| 공용면적 | CMAR | Common Area | +| 공통 | CMMN | Commonness | +| 관 | PP | Pipe | +| 관계 | REL | Relation | +| 관련 | RLTN | Relation | +| 관로 | PPLINE | Pipe Line | +| 관리 | MNG | Management | +| 관리기관 | MGC | Management Agency | +| 관리자 | MNGR | Manager | +| 관찰 | WTCH | Watch | +| 관측 | OBSR | Observation | +| 관측소 | OBSRVT | Observatory | +| 교반기 | AGITOR | Agirator | +| 교정 | CRRC | Correction | +| 구간 | SCTN | Section | +| 구동 | DRIVE | Drive | +| 구분 | SE | Section | +| 구성 | CMPSG | Composing | +| 구역 | ZONE | Zone | +| 구조 | STRCT | Structure | +| 권역 | DSTR | District | +| 권한 | AUTH | Authority | +| 규격 | STNDRD | Standard | +| 규모 | SCAL | Scale | +| 규제 | REGL | Regulation | +| 균등 | EQLTY | Equality | +| 금액 | AMT | Amount | +| 급수 | WSP | Water Supply | +| 기간 | WHL | While | +| 기관 | INST | Institution | +| 기록 | RECD | Recording | +| 기록부 | REGI | Register | +| 기밀 | SCRET | Secret | +| 기법 | TECQU | Techniques | +| 기본 | BSIC | Basics | +| 기본정보 | BASINF | Bassinfo | +| 기본키 | PK | Primary Key | +| 기상 | WETHER | Weather | +| 기안 | DRFTG | Drafting | +| 기재 | WRIT | Write | +| 기점 | CDPNT | Cardinal Point | +| 기존 | LGCY | Legacy | +| 기준 | CRTR | Criteria | +| 기타 | ETC | Etc | +| 기한 | TERM | Term | +| 기후 | CLMT | Climate | +| 긴급 | EMRG | Emergency | +| 날개벽 | WW | Wing Wall | +| 날씨 | WETHR | Weather | +| 날짜 | DATE | DATE | +| 남조류 | BLAL | Blue-Green Algae | +| 납 | PB | Plumbum | +| 내려받기 | DWLD | Download | +| 내부반송 | ITNRCC | Internal Recycl | +| 내용 | CN | Content | +| 내용연수 | SVLF | Service Life | +| 냄새 | SMELL | Smell | +| 년 | YY | Year | +| 년간 | FAYR | For A Year | +| 노즐 | NZL | Nozzle | +| 노출 | EXPSR | Exposure | +| 농도 | DNSTY | Density | +| 농축 | TKN | Thickened | +| 누적 | ACML | Accumulate | +| 누출 | LKG | Leakage | +| 눈 | SNW | Snow | +| 단가 | UNPRC | Unit Price | +| 단기 | SRTPD | Short Period | +| 단순 | SIMPL | Simplicity | +| 단위 | UNIT | Unit | +| 대구역 | LZONE | Large Zone | +| 대분류 | LCLAS | Large Classification | +| 대장 | REGSTR | Register | +| 대장균 | COLI | Coliform, Fecal Coliform | +| 대장균군 | COLIB | Coliform Bacteria | +| 대지면적 | SIAR | Site Area | +| 대체 | RPLC | Replace | +| 덮개 | COVR | Cover | +| 데이타 | DAT | Data | +| 도구 | UNT | Unit | +| 도래 | ADVNT | Advent | +| 도로 | ROAD | Road | +| 도로명 | RN | Road Name | +| 도시 | CTY | City | +| 도엽 | MAPDMC | Map Demarcation | +| 도표 | CHART | Chart | +| 독성 | TOXCTY | Toxicity | +| 동의 | AGRE | Agreement | +| 동절기 | WSESN | The Winter Season | +| 등급 | GRD | Grade | +| 등기 | RG | Registered | +| 등록 | REG | Registration | +| 디바이스 | DVICE | Device | +| 디코딩 | DCD | Decoding | +| 레이어 | LYR | Layer | +| 로그인 | LGN | Login | +| 로우 | ROW | Row | +| 마감 | DDLN | Deadline | +| 마이크로필터 | MCRFT | Micro Filter | +| 막 | MBRN | Membrane | +| 만족도 | STFDG | Satisfaction Degree | +| 맑음 | CLAR | Clear | +| 매각 | SAL | Sale | +| 맨홀 | MH | Manhole | +| 맵 | MAP | Map | +| 메인 | MAIN | Main | +| 메일 | EMAIL | Email | +| 메탄올 | MTN | Methanol(CH3OH) | +| 명 | NM | Name | +| 모니터링 | MTRG | Monitoring | +| 모델 | MODL | Model | +| 모델링 | MODLNG | Modeling | +| 모래 | SAND | Sand | +| 목 | MOK | Mok | +| 무산소 | ANX | Anoxic | +| 무산소조 | ANX_TANK | Anoxic Tank | +| 무인센서 | MNLSENSR | Manless Sensor | +| 문서 | DCMT | Document | +| 물 | WTR | Water | +| 미급수 | NONWTRSPL | Non Water Supply | +| 미생물 | BLGC | Biological | +| 미세먼지 | PM | Particulate Matter | +| 미세목 | MCRB | Micro Bar | +| 민간위탁 | ETSVN | Entrustment To Civilian | +| 민감 | SST | Sensitive | +| 민원 | CVLCPT | Civil Complaint | +| 바이러스 | VIRS | Virus | +| 반기 | HT | Half Term | +| 반류수 | SIDSWT | Side Stream Water | +| 반송 | SNDBK | Send Back | +| 반송수 | RCCWT | Recycled Water | +| 반송슬러지 | RTNSLG | Return Sludge | +| 반영 | RFLT | Reflection | +| 반응조 | RETNK | Reactant Tank | +| 반출 | EXPRT | Export | +| 반토 | ALUM | Alumina | +| 반환 | GVBK | Giving Back | +| 받이 | INL | Inlet | +| 발생 | OCRN | Occurrence | +| 발전 | PWGNRT | Power Generation | +| 발주기관 | ORNT | Order Institution | +| 방류 | DCWTR | Discharge Water Stock Etc | +| 방류구 | DSCHG | Discharge | +| 방류수 | EFLEC | Effluence | +| 방문 | VST | Visit | +| 방문자 | VSTR | Visitor | +| 방법 | MTHD | Method | +| 방사 | RLSE | Release | +| 배관 | PLINE | Pipeline | +| 배분 | DSTRB | Distirbution | +| 배설물 | EXCRTN | Excretion | +| 배수 | DRNG | Drainage | +| 배수구역 | DRANGZN | Drainage Zone | +| 배출 | EXHST | Exhaust | +| 배출가스 | EMGA | Emission Gases | +| 배출구 | VENT | Vent | +| 배출시설 | DHGP | Discharge Equipment | +| 배출시험 | EXHTST | Exhaust Test | +| 배출자 | DCHGR | Discharge Person | +| 밸브 | VALVE | Valve | +| 번호 | NO | Number | +| 법 | LAW | Law | +| 법적 | LGL | Legal | +| 법정 | STTY | Statutory | +| 법정동 | STDG | Statutory Dong(Administrative District) | +| 법정리 | STLI | Statutory Li(Administrative District) | +| 변경 | CHG | Change | +| 변동 | FLCTN | Fluctuation | +| 변속기 | TRMIS | Transmission | +| 보고 | RPT | Report | +| 보고서 | RPOT | Report | +| 보관 | STORG | Storage | +| 보수 | MEND | Mend | +| 보유 | HOLD | Hold | +| 보정 | RVSION | Revision | +| 보조 | ASSISC | Assistance | +| 보조금 | SBSIDY | Subsidy | +| 복개 | COVE | Cover | +| 복토 | CWTH | Covering With Soil | +| 본류 | MI_RV | Main River | +| 본번 | MNO | Main Number | +| 부검 | ATPSY | Autopsy | +| 부과 | LEVY | Levy | +| 부과금 | LVYAMT | Levy Amount | +| 부기 | BUKIP | Bookkeeping | +| 부담금 | ALOTM | Allotment | +| 부번 | SNO | Sub Number | +| 부산물 | BYPRD | By-Product | +| 부서 | DEPT | Department | +| 부숙화 | DECPZ | Decomposed ~Ize | +| 부적합 | IMPPRTY | Impropriety | +| 부착 | ASN | Adhesion | +| 부품 | CMPNT | Components | +| 부하량 | LOADQTY | Load Quantity | +| 분과 | DIVSN | Division | +| 분뇨 | ENSO | Excreta, Night Soil | +| 분류 | CL | Classification | +| 분류군 | TAXON | Taxon | +| 분리막 | SPRTMBR | Separation Membrane | +| 분배 | DTRBT | Distribution | +| 분배조 | DTRBT_TANK | Distribution Tank | +| 분야 | RLM | Realm | +| 분원성 | FECL | Fecal | +| 분원성대장균군 | FCOLI | Fecal Coliform | +| 분원성연쇄상구균 | FCSTRCCI | Fecal Streptococci | +| 분할 | PRTIN | Partition | +| 비밀번호 | PSWD | Password | +| 비상 | EMGNY | Emergency | +| 비정상 | ABNRM | Abnormality | +| 사양 | SPECS | Specification | +| 사업비 | WCT | Working Cost | +| 사업장 | BPLC | Business Place | +| 사여과지 | SAND_ FILTR | Sand Filter | +| 사용 | USE | Use | +| 사용가능 | USABLE | Usable | +| 사용권 | RIOUS | Right Of Using | +| 사용량 | USG | USAGE | +| 사용자 | USER | User | +| 사위 | FDG | Four Directions Girth | +| 사유 | RESN | Reason | +| 사이클 | CYCLE | Cycle | +| 사이트 | SITE | Site | +| 사진 | PHOT | Photograph | +| 사항 | MATTR | Matter | +| 사후 | AFTFAT | After Fact | +| 삭제 | DEL | Deletion | +| 산기관 | DFS | Diffuser | +| 산업 | INDUST | Industrial | +| 산화환원전위 | ORP | Oxidation Reduction Potential | +| 살모넬라 | SLMN | Salmonella | +| 삼원촉매장치 | TWCTLC | Three Way Catalytic Converter | +| 상세 | DTL | Details | +| 상세주소 | DADDR | Detailed Address | +| 상수 | WT | Water | +| 상업지역 | CMCL_ARA | Commercial Area | +| 상태 | STUS | Status | +| 상품 | GDS | Goods | +| 상향류 | UFW | Upflow | +| 상호 | CONM | Company Name | +| 색도 | CRMTY | Chromaticity | +| 생략 | OMS | Omission | +| 생물 | LVB | Living Being | +| 생물반응조 | BIRAT | Bioreactor | +| 생물서식처 | ORGNHBT | Organism Habitat | +| 생물종 | BISP | Biological Species | +| 생성 | CRT | Creation | +| 생슬러지 | RSLG | Raw Sludge | +| 생태 | ECLGY | Ecology | +| 생태독성 | ECTOX | ecotoxicity | +| 서류 | PAPE | Papers | +| 서명 | SIGN | Signing | +| 서버 | SRVR | Server | +| 서비스 | SRVC | Service | +| 설계 | DESGN | Design | +| 설계용량 | DESGN_CPCT | Design Capacity | +| 설립 | FNDN | Foundation | +| 설정 | STNG | Setting | +| 설치 | INSTL | Installation | +| 성격 | CHTR | Character | +| 성공 | SCS | Success | +| 성능 | PRFMNC | Performance | +| 성능검사 | EFTST | Efficiency Test | +| 성적서 | SCRRP | Score Report | +| 세균 | BCLS | Bacillus | +| 세목 | FB | Fine bar | +| 세부 | DETAIL | Detail | +| 세정 | WS | Washing | +| 세정제 | CNML | Cleaning Material | +| 소 | SML | Small | +| 소독 | DSNF | Disinfection | +| 소독제 | DSINF | Disinfectant | +| 소분류 | SCLAS | Small Classification | +| 소수점 | DCPT | Decimal Point | +| 소요 | REQ | Required | +| 소유 | OWN | Own | +| 소유역 | SBASIN | Sub Basin | +| 소재지 | PLAC | Place Of Location | +| 소포 | ATFM | Antifoaming | +| 소포수 | FBW | Foam Breaking Water | +| 소포제 | ANAG | Antifoaming Agent | +| 송부 | SENDNG | Sending | +| 송신 | SNDM | Send Message | +| 송풍기 | BLW | Blower | +| 수 | CNT | Count | +| 수거 | TKAWY | Take Away | +| 수계 | WWBH | Watershed, Water Basin, Hydrosphere | +| 수동 | HOPR | Hand Operation | +| 수리학적 체류시간 | HRT | Hydraulic Retention Time | +| 수문 | Gate | Gate | +| 수산화나트륨 | SOHY | Sodium Hydroxide | +| 수소이온 | HION | Hydrogen Ion | +| 수신 | RCPTN | Reception | +| 수용가 | RCPTR | Receptor | +| 수위 | WAL | Water Level | +| 수정 | MDFCN | Modification | +| 수정자 | MDFR | Modifier | +| 수제 | SPRDIKE | Superdike | +| 수조 | Tank | Tank | +| 수질 | QTWT | Quality Of Water | +| 수질개선 | WTRQLIMP | Water Quality Improvement | +| 수질기준 | WQST | Water Quality Standards | +| 수질배출정보 | WTDI | Water Discharge Information | +| 수집 | CLCT | Collection | +| 순환 | CCL | Cycling | +| 쉬겔라 | SHIGLA | Shigella | +| 스컴 | SCM | Scum | +| 스크린 | SCR | Screen | +| 슬러지 | SLG | Sludge | +| 슬러지 밀도지수 | SDI | sludge density index | +| 슬러지 용량 지표 | SVI | Sludge Volume Index | +| 습지 | WTLN | Wetland | +| 승인 | APRV | Approval | +| 승인권 | APPRVRGT | Right Of Approval | +| 시각 | TIME | Time | +| 시간 | HR | Hour | +| 시군구 | SGG | Si/Gun/Gu | +| 시나리오 | SCEN | Scenario | +| 시도 | CTPV | Cities And Provinces | +| 시료 | SMPL | Sample | +| 시설 | FCLT | Facility | +| 시설면적 | FCAR | Facility Area | +| 시설물 | FCLTS | Facilities | +| 시스템 | SYS | System | +| 시안 | CYN | Cyaan | +| 시운전 | TSTRN | Test Run | +| 시작 | BGNG | Beginning | +| 시점 | PTM | Point Time | +| 시행 | ENFC | Enforcement | +| 시험 | TEST | Test | +| 신고 | DCLR | Declaration | +| 신고자 | DCL | Declarer | +| 신규 | NEW | New | +| 신기술 | NTCH | New Technology | +| 신뢰 | CNFDNC | Confidence | +| 신뢰성 | CRDIBLTY | Credibility | +| 실시 | OPRT | Operation | +| 실적 | ACRS | Actual Result | +| 실행 | EXCN | Execution | +| 심사 | SRNG | Screening | +| 심의 | DLBR | Deliberation | +| 아황산환원혐기성포자형성균 | SFSRA | Spore Forming Sulfite Reducing Anaerobes | +| 안정화 | STBLZT | Stabilization | +| 암롤박스(컨테이너) | ARTCT | Arm roll type container | +| 암모니아성질소 | NHN | Ammonia Nitrogen | +| 압력 | PRESSR | Pressure | +| 압송 | PUMPNG | Pumping | +| 압축 | CMPRS | Compress | +| 액비 | LQFERTLZ | Liquid Fertilizer | +| 액상 | LIQD | Liquid | +| 약 | GENTLE | Gentle | +| 약품 | CMC | Chemical | +| 약품비 | MEDFEE | Medicine Fee | +| 약품사용 | USMD | Use Medicines | +| 업로드 | UPLD | Upload | +| 업무 | DTY | Duty | +| 없음 | NOT | Nothing | +| 에너지 | ENR | Energy | +| 에틸벤젠 | ETBNZN | Ethylbenzene | +| 여과 | FILTR | Filteration | +| 여과수 | FTWT | Filtered Water | +| 여과재 | FTMTRL | Filter Material | +| 여과형 | FILTRFOM | Filter Form | +| 여부 | YN | Yes or No | +| 여시니아균 | YERSNA | Yersinia | +| 역률 | PWRER_FCT | Power Factor | +| 역세척 | BWS | Back Wash | +| 연결 | CONN | Connection | +| 연계 | LINK | Link | +| 연도 | YR | Year | +| 연락 | CNTC | Contact | +| 연령 | AGE | Age | +| 연료화 | FUELZ | Fuelize | +| 연월 | YM | Year and Month | +| 염소 | CLRN | Chlorine | +| 염소소독 | CHDI | Chlorine Disinfection | +| 염소이온 | CLION | Chlorine Ion | +| 염화제이철 | IRCLD | Iron(III) Chloride | +| 예보 | FRCST | Forecast | +| 예비 | PRPT | Preparation | +| 예산 | BGT | Budget | +| 예상 | EXPCT | Expectation | +| 예약 | RSVT | Reservation | +| 예외 | EXCPT | Exception | +| 예정 | PRNG | Prearrangement | +| 예찰 | PRDICT | Prediction | +| 오류 | ERR | Error | +| 오수받이 | HUS_INL | House Inlet | +| 오염물질 | CNTMN | Contaminant | +| 오염원 | POLUSRC | Pollution Source | +| 오존 | OZN | Ozone | +| 오프로드 | OFFRD | Off Road | +| 옥살산 | OXACID | Oxalic Acid | +| 와류 | VORTEX | Vortex | +| 와류형 | VORTXFOM | Vortex From | +| 완결 | COMPLT | Completion | +| 완료 | CMPTN | Completion | +| 완전 | PRFECT | Perfection | +| 요건 | REQSIT | Requisite | +| 요막액 | ALLNTFLD | Allantoic Fluid | +| 요청 | DMND | Demand | +| 용량 | CPCT | Capacity | +| 용역 | SERVC | Service | +| 용존산소 | DO | Dissolved Oxygen | +| 용해 | DSLV | Dissolving | +| 우량 | EXLT | Excellent | +| 우수 | ST | Storm | +| 우수받이 | STRT_INL | Street Inlet | +| 우수토구 | STM_OVFL_MUT | Storm Overflow Mouth | +| 우회 | BP | BY PASS | +| 운반 | CNVY | Conveyance | +| 운영 | OPER | Operation | +| 운영비 | OPEXP | Operation Expense | +| 운행 | OPRAT | Operation | +| 원격 | RMRT | Rmrt | +| 원격감시체계 | TMS | Tele Monitoring System | +| 원료 | MTRAL | Material | +| 원수 | ORGWT | Origin'S Water | +| 원인자 | ORGPR | The Origin Person | +| 월 | MM | Month | +| 위도 | LAT | Latitude | +| 위반 | CNTR | Contravene | +| 위촉 | ENTRST | Entrusting | +| 위치 | LOC | Location | +| 위탁 | CNSG | Consign | +| 위탁자 | CONSGNR | Consignor | +| 유기인 | ORGP | Organic Phosphorus | +| 유량 | FLUX | Flux | +| 유량조정조 | EQLZT__TANK | Equalization Tank | +| 유입 | INFlOW | Inflow | +| 유입수 | INFWTR | Influent Water | +| 유주 | SWARMG | Swarming | +| 유지 | MNTNC | Maintenance | +| 유지용수공급 | MWRS | Maintenance Water Requirement Supply | +| 유체 | FLUID | Fluid | +| 유출 | EFFLUX | Efflux | +| 유충 | LAVL | Larval | +| 유형 | TY | Type | +| 유효 | VLD | Validity | +| 음용 | DKPS | For Drinking Purpose | +| 읍면동 | EMD | Eup/Myun/Dong | +| 응답 | RSPNS | Response | +| 응집제 | COAG | Cohesive Agents | +| 이관 | TRNSF | Transfer | +| 이동 | MVMN | Movement | +| 이력 | HST | History | +| 이메일 | EML | Email | +| 이상 | ABOVE | Above | +| 이송 | TRSF | Transfer | +| 이의 | OBJC | Objection | +| 이차 | SCDR | Secondary | +| 이차침전지 | SCDR_SDMT_TANK | Secondary Sedimaentation | +| 이행 | MIG | Migratation | +| 인건비 | PEXP | Personnel Expenses{Expenditure] | +| 인계 | HNDVR | Handover | +| 인계서 | HDOVDC | Handover Document | +| 인구 | POPL | Population | +| 인산 | PHACID | Phosphoric Acid | +| 인산염인 | POP | phosphate | +| 인양기 | LFT | Lifter | +| 인원수 | NOPE | The Number of Persons | +| 인증 | CERT | Certification | +| 인처리 | PPRCS | Phosphorus Process | +| 인허가 | APVL | Approval | +| 일 | DY | Day | +| 일련번호 | SN | Serial Number | +| 일반 | GNRL | General | +| 일별 | DALY | Daily | +| 일시 | DT | Date and Time | +| 일자 | YMD | YEAR Month Day | +| 일지 | DIARY | Diary | +| 일차 | PRM | Primary | +| 일차침전지 | PST_SDMT_TANK | Primary Sedimentation Tank | +| 일평균 | DYRG | Day Average | +| 임시 | TMPR | Temporary | +| 임의 | RNDM | Random | +| 입고 | WRHS | Warehousing | +| 입력 | INPT | Input | +| 잉여 | SPS | Surplus | +| 잉여슬러지 | EC_SLG | Excess Sludge | +| 자 | MAN | Man | +| 자갈 | GRAVEL | Gravel | +| 자료 | DATA | Data | +| 자리 | DIGIT | Digit | +| 자립화 | SLFRLI | Self Reliance | +| 자연형 | NATRLFOM | Natural Form | +| 자외선 | URLI | Ultraviolet Rays, Light | +| 자재 | MTRIL | Material | +| 자체 | ITSL | Itself | +| 작성 | WRT | Writing | +| 작업 | JOB | Job | +| 잠금 | LOCK | Lock | +| 장기 | LNGTR | Long Term | +| 장소 | PLCE | Place | +| 장착 | MNTNG | Mounting | +| 재 | RE | Re | +| 재이용 | REUSE | Reuse | +| 재이용수 | | Reuse Water | +| 재활용 | RUSE | Reuse | +| 저감 | REDUC | Reduction | +| 저류조 | DTT_TANK | Detention Tank | +| 저온 | LOWTMP | Low Temperature | +| 저장 | STRG | Storage | +| 저장소 | STORGE | Storage | +| 적발 | DSCL | Disclose | +| 적산 | ADD | Addition | +| 적용 | APLCN | Application | +| 적합 | STBLT | Suitability | +| 전기 | ELCTY | Electricity | +| 전기료 | ELFEE | Electricity Fee | +| 전력 | PWRER | Power | +| 전문 | SPCY | Specially | +| 전산 | CMPUTT | Computation | +| 전송 | TRSM | Transmission | +| 전압 | VLTGE | Voltage | +| 전용 | EXUSE | Exclusive Use | +| 전원 | SELCT | Source Of Electricity | +| 전처리 | PRETR | Pretreatment | +| 점검 | CHCK | Check | +| 접속 | CNTN | Connection | +| 정밀 | PRCIS | Precision | +| 정보 | INFO | Information | +| 정산 | CLCLN | Calculation | +| 정상 | NRMLT | Normality | +| 정수장 | WPPW | Water Purification Plant, Waterworks | +| 정제 | REFINE | Refining | +| 정지 | STP | Stop | +| 제거 | RMV | Removal | +| 제수변 | SL_VALVE | Sluice Valve | +| 제작 | MNFCT | Manufacture | +| 제조사 | MKR | Maker | +| 제출 | SBMSN | Submission | +| 제한 | LIMT | Limitation | +| 조류 | ALG | Algae | +| 조목 | CSB | Coarse Bar | +| 조사 | EXMN | Examination | +| 조성 | MAKE | Make | +| 조정 | EQLT | Equalization | +| 조치 | ACTN | Action | +| 조회 | INQ | Inquiry | +| 존재 | EXST | Existence | +| 종결 | TRMN | Termination | +| 종균제 | MICBA | Microbial Agent | +| 종료 | END | End | +| 종류 | KND | Kind | +| 종목 | IEM | Item | +| 종별 | ASORT | Assortment | +| 종사자 | ENFSN | Enfilade Person | +| 종점 | TMNL | Terminal | +| 좌표 | COOR | Coordinate | +| 주간 | WIK | Week | +| 주변 | CFR | Circumference | +| 주소 | ADDR | Address | +| 주입 | IJTN | Injection | +| 주파수 | FQNC | Frequency | +| 준공 | CMCN | Completion Of Construction | +| 중권역 | MDSTRC | Medium District | +| 중복 | DPCN | Duplication | +| 중분류 | MLSFC | Middle Classification | +| 중온 | MESPH | Mesophilic | +| 중첩 | RETRTN | Reiteration | +| 중화설비 | NUTEQP | Neutralization Equipment | +| 중화제 | CNTAG | Counteragent | +| 증가 | INCRS | Increase | +| 지도점검표사본 | CITBMR | Copy of Instruction Checklist | +| 지방자치단체 | LGOV | Local Governments | +| 지번 | LOTNO | Lot Number | +| 지상 | GRND | Ground | +| 지역 | ARA | Area | +| 지질 | NTRFS | Nature Of Soil | +| 지질도 | GEOMAP | Geological Map | +| 지하 | UDGD | Underground | +| 지하수 | GROU | Groundwater | +| 지형 | TPGRPH | Topography | +| 진공 | VCM | Vacuum | +| 진단 | DGNSS | Diagnosis | +| 진동 | VIBRAT | Vibration | +| 진행 | PRGRE | Progress | +| 질산 | NIACID | Nitric Acid. | +| 질산성질소 | NO3N | Nitrate Nitrogen | +| 집계 | TOT | Total | +| 집계구 | SUMDST | Summation District | +| 집행 | IMPL | Implementation | +| 찌꺼기 | LEES | Lees | +| 차아염소산 | NAOCL | Hypochlorous Acid | +| 차집 | ITCT | Intercepting | +| 착공 | BGNCST | Begin Construction | +| 채수 | SAMWT | Sampling Of Water | +| 채수기 | WTRSMPLR | Water Sampler | +| 채수병 | WTRBOTL | Water Bottle | +| 처리 | PRCS | Process | +| 처리비 | PSSFEE | Process Fee | +| 처리수 | TRT_WT | Treated Water | +| 처리수조 | TRT_WT_TANK | Treated Water Tank | +| 처리시설 | TRTMTFCLT | Treatment Facility | +| 처리자 | PRCR | Processor | +| 처분 | DSPS | Disposition | +| 철염 | FESLT | Ferric Salt | +| 첨부 | ATCH | Attachment | +| 첨부파일 | ATFI | Attachment File | +| 초과 | EXCS | Excess | +| 초기화 | INITL | Initialization | +| 촉매 | CTLY | Catalyst | +| 총계 | TSM | Summation | +| 총대장균군 | TCOLFGRP | Total Coliform Group | +| 총량 | TQT | Total Quantity | +| 총면적 | GFA | Gross Floor Area | +| 총사업비 | TPCO | Total Project Cost | +| 총인구 | TPOPLTN | Total Population | +| 총인처리시설 | TPFCT | Total Phosphorus facility | +| 최근 | RCNT | Recent | +| 최대값 | MAXV | Maximum Value | +| 최소값 | MINV | Minimum Value | +| 최적 | BMTB | Being The Most Suitable | +| 최종 | LAST | Last | +| 최초 | FRST | First | +| 추출 | EXTR | Extraction | +| 추출정보 | EXTRINFO | Extraction Information | +| 축 | AXS | Axis | +| 출력 | OTPT | Output | +| 출발 | LVE | Leave | +| 충전기 | CHRGR | Charger | +| 취득 | ACQS | Acquisition | +| 취소 | RTRCN | Retraction | +| 측정 | MSUR | Measurement | +| 측정기 | MSRR | Measurement Instrument | +| 측정소 | MSRSTN | Measurement Station | +| 침사지 | GTCB | Grit Chamber | +| 침전 | SDMT | Sediment | +| 침전지 | SDMT_TANK | sedimentation tank | +| 컨베이어 | CVY | Conveyor | +| 컬럼 | COL | Column | +| 코드 | CD | Code | +| 크기 | SZ | Size | +| 크실렌 | XYLEN | Xylene | +| 탄화 | CARB | Carbonization | +| 탈거 | CRYOFF | Carry Off | +| 탈리액 | SPNT | Supernatant | +| 탈수 | DWTR | Dewatering | +| 탈수기 | DEHYM | Dehydration Machine | +| 탈질 | DNTFCT | Denitrificatio | +| 탈취 | DODRZ | Deodorization | +| 탈취기 | DEODRZ | Deodorizer | +| 탈취제 | DEODR | Deodorant | +| 탈황제 | DSLFA | Desulfurizing Agent | +| 터널형 | YUNLTP | Tunnel Type | +| 테이블 | TBL | Table | +| 토구 | OTFL | Outfall | +| 토지 | LAD | Land | +| 토지면적 | LDAR | Land Area | +| 통계 | STATS | Statistics | +| 통보 | NTFCTN | Notification | +| 통신 | COMM | Communication | +| 통지 | AVTSMT | Avertissement | +| 통합 | UNTY | Unity | +| 특성 | CHRT | Character | +| 특수 | SPECL | Special | +| 특정유해물질 | SHCH | Specified Hazardous Chemicals | +| 파일 | FILE | File | +| 판정 | JGMT | Judgement | +| 펌프 | PUMP | Pump | +| 펌프장 | PUMP_STT | Pump Station | +| 페놀 | PHNL | Phenol | +| 평가 | EVL | Evaluation | +| 폐기 | DSCD | Discard | +| 폐기물 | WSTE | Waste | +| 폐쇄 | CLSG | Closing | +| 폐수 | WAWA | Waste Water | +| 폐수처리 | WSWT | Wastewater Treatment | +| 폐열 | WASTHET | Waste Heat | +| 포기 | ARTN | Aeration | +| 포트 | PORT | Port | +| 폭기조 | ARTN_TANK | Aeration Tank | +| 폴리머 | PLYMR | Polymer | +| 폴리철 | FRRCSU | Ferric Sulfate | +| 표고 | AL | Altitude | +| 표기 | MARK | Mark | +| 표시 | INDCT | Indication | +| 표준 | STD | Standard | +| 표출 | EPRSS | Expression | +| 플래그 | FLAG | Flag | +| 필드 | FILD | Field | +| 필수 | ESNTL | Essential | +| 필증 | CERFIN | Certificate Of Finishing | +| 필터 | FLTER | Filter | +| 하수 | SWGE | Sewage | +| 하수도 | DRNGSYS | Drainage System | +| 하수미처리 | SEWGUNTTM | Un Sewage Treatment | +| 하수처리 | SEWGTRTM | Sewage Treatment | +| 하수처리수 | STWTR | Sewage Treatment Water | +| 하위 | LOWR | Lower | +| 하향류 | DWFW | Downflow | +| 할인 | DSCNT | Discount | +| 함수율 | MISTR_CTNT | Moisture content | +| 합계 | SMM | Summation | +| 해당 | RLVT | Relevant | +| 해석 | INTRPRTT | Interpretation | +| 해양투기 | OCDU | Ocean Dumping | +| 행망 | ADMNW | Administration Network | +| 행정 | ADMN | Administration | +| 행정구역 | ADMI | Administrative District | +| 행정동 | DONG | Dong (Administrative District) | +| 허가 | PRMSN | Permission | +| 허용 | PERM | Permission | +| 현상 | PHNMN | Phenomenon | +| 혐기 | AARB | Anaerobic | +| 혐기조 | AARB_TANK | Anaerobic tank | +| 협잡물 | CCMT | Concomitant | +| 형태 | SHAP | Shape | +| 호기 | ARB | Aerobic | +| 호기조 | ARB_TANK | Aerobic Tank | +| 호소수 | LMSHWT | Lake And Marsh Water | +| 호수 | LKE | Lake | +| 호안 | RVTMNT | Revetment | +| 호이스트 | HIST | Hoist | +| 혼합 | CMPN | Compound | +| 혼합액현탁고형물 | MLSS | Mixed liquor suspended solids | +| 혼화지 | MXG_TNAK | Mixing tank | +| 홈페이지 | HMPG | Homepage | +| 홍수 | FLOOD | Flood | +| 확인 | IDNTY | Identify | +| 확인서 | CNFRMN | Confirmation | +| 확인자 | IDFR | Identifier | +| 확장자 | EXTN | Extension | +| 확정 | CFMTN | Confirmation | +| 환경 | ENVR | Environment | +| 환경기초시설 | ENINF | Environmental Infrastructure | +| 환원 | RSTORE | Restoration | +| 활성탄 | AC | Activated Carbon | +| 활성화 | PRMT | Promote/Facilitate | +| 활용 | PRUS | Practical Use | +| 황산 | ECCBT | Encyclopaedia Britannica (H2So4) | +| 회사 | CO | Company | +| 회수 | RTRVL | Retrieval | +| 회신 | RPLY | Reply | +| 효율 | EFC | Efficiency | +| 효율성 | EFFCTV | Effectiveness | +| 휴대 | MBL | Mobile | +| 흡수정 | EXTTW | Extraction Well | +| 흡착 | ADST | Adsorption | diff --git a/apps/cs/next.config.ts b/apps/cs/next.config.ts index d75960377..61d7f72cc 100644 --- a/apps/cs/next.config.ts +++ b/apps/cs/next.config.ts @@ -5,6 +5,20 @@ const subDomain = const nextConfig: NextConfig = { output: "standalone", + experimental: { + proxyTimeout: 600000, + }, + eslint: { + ignoreDuringBuilds: true, + }, + typescript: { + ignoreBuildErrors: true, + }, + logging: { + fetches: { + fullUrl: true, + }, + }, async headers() { return [ { diff --git a/apps/cs/src/pages/admin/ui/add-admin-dialog.tsx b/apps/cs/src/pages/admin/ui/add-admin-dialog.tsx index 859af4058..c49d75530 100644 --- a/apps/cs/src/pages/admin/ui/add-admin-dialog.tsx +++ b/apps/cs/src/pages/admin/ui/add-admin-dialog.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import { keepPreviousData, useQuery, @@ -75,7 +75,7 @@ function AddAdminDialog() { }); const query = useQuery({ - queryKey: [ENDPOINT.USER_SERVICE.USERS, pagination, "normal-user"], + queryKey: [ENDPOINT.USER_SERVICE.USERS, "findPageUser", pagination], queryFn: () => UserApi.findPageUser({ ...userSearchQuery, @@ -181,6 +181,21 @@ function AddAdminDialog() { query.refetch(); }; + useEffect(() => { + if (!open) { + setRowSelection({}); + setSelectedGroupId(""); + setUserSearchQuery({ + keywordType: "userName", + keyword: "", + }); + setPagination({ pageIndex: 0, pageSize: 10 }); + queryClient.removeQueries({ + queryKey: [ENDPOINT.USER_SERVICE.USERS, "findPageUser"], + }); + } + }, [open]); + return ( diff --git a/apps/cs/src/pages/board-statistics/api/board-statistics-service.ts b/apps/cs/src/pages/board-statistics/api/board-statistics-service.ts index 03291223c..9380132e8 100644 --- a/apps/cs/src/pages/board-statistics/api/board-statistics-service.ts +++ b/apps/cs/src/pages/board-statistics/api/board-statistics-service.ts @@ -1,53 +1,22 @@ -// import { client } from "@/shared/api"; -// import { ENDPOINT } from "@/shared/config"; +import { client } from "@/shared/api"; +import { ENDPOINT } from "@/shared/config"; import { type BoardStatisticsParams, type BoardStatistics, type BoardStatisticsService, } from "../model/board-statistics-interface"; -// 임시 모의 데이터 -const mockBoardStatisticsData: BoardStatistics = { - totalPosts: 2456, - totalComments: 156, - averageRating: 4.2, - totalViews: 45678, - dayList: [ - { date: "1월", posts: 10, comments: 5, views: 800 }, - { date: "2월", posts: 15, comments: 8, views: 1200 }, - { date: "3월", posts: 12, comments: 10, views: 1000 }, - { date: "4월", posts: 18, comments: 15, views: 1500 }, - { date: "5월", posts: 22, comments: 20, views: 1800 }, - { date: "6월", posts: 20, comments: 18, views: 1600 }, - { date: "7월", posts: 25, comments: 22, views: 2000 }, - ], - boardList: [ - { boardType: "notice", boardName: "공지사항", count: 45 }, - { boardType: "faq", boardName: "FAQ", count: 120 }, - { boardType: "qna", boardName: "Q&A", count: 180 }, - { boardType: "free", boardName: "자유게시판", count: 95 }, - ], - statusList: [ - { status: "completed", statusName: "답변완료", count: 75 }, - { status: "waiting", statusName: "답변대기", count: 25 }, - ], -}; - class BoardStatisticsApiService implements BoardStatisticsService { async getBoardStatistics( params: BoardStatisticsParams, ): Promise { - console.log("params: ", params); try { // 실제 API 연동 시 아래 코드 사용 - // const response = await client.get( - // `${ENDPOINT.CMS_SERVICE.BOARD_STATISTICS}`, - // { params }, - // ); - // return response.data; - - // 임시 모의 데이터 반환 - return Promise.resolve(mockBoardStatisticsData); + const response = await client.get( + `${ENDPOINT.BOARD_SERVICE.STATISTICS}`, + { params }, + ); + return response.data; } catch (error) { console.error("게시판 통계 데이터 조회 실패:", error); throw error; diff --git a/apps/cs/src/pages/board-statistics/model/board-statistics-interface.ts b/apps/cs/src/pages/board-statistics/model/board-statistics-interface.ts index 1f113475f..3d7a97c87 100644 --- a/apps/cs/src/pages/board-statistics/model/board-statistics-interface.ts +++ b/apps/cs/src/pages/board-statistics/model/board-statistics-interface.ts @@ -12,32 +12,36 @@ export interface BoardStatisticsParams extends PageableParams { } export interface BoardStatistics { - totalPosts: number; - totalComments: number; - averageRating: number; - totalViews: number; - dayList: DayStatistics[]; - boardList: BoardTypeStatistics[]; - statusList: StatusStatistics[]; + bbsTotalDto: BbsTotalDto; // 게시판 전체 통계 + pstAnsList: PstAnswerStatistics[]; // 게시물 답변 상태별 통계 목록 + bbsDailyList: BbsDailyStatistics[]; // 게시판 일별 통계 목록 + bbsPstCntList: BbsPostCountStatistics[]; // 게시판별 게시물 건수 통계 목록 } -interface DayStatistics { - date: string; - posts: number; - comments: number; - views: number; +interface BbsTotalDto { + pstInqCnt: number; // 게시물 조회 건수 + pstEvlAvg: number; // 게시물 평가 평균 + pstTotCnt: number; // 게시물 총 건수 + pstAnsWaitCnt: number; // 게시물 답변 대기 건수 } -interface BoardTypeStatistics { - boardType: string; - boardName: string; - count: number; +interface PstAnswerStatistics { + answerNm: string; // 답변 명 + answerYn: string; // 답변 여부 (Y/N) + ansCnt: number; // 답변 건수 } -interface StatusStatistics { - status: string; - statusName: string; - count: number; +interface BbsDailyStatistics { + dayYmd: string; // 일자 (YYYY-MM-DD) + pstInqCnt: number; // 게시물 조회 건수 + pstCmntCnt: number; // 게시물 댓글 건수 + pstNewCnt: number; // 게시물 신규 건수 +} + +interface BbsPostCountStatistics { + bbsId: number; // 게시판 ID + bbsNm: string; // 게시판 명 + pstCnt: number; // 게시물 건수 } export interface BoardStatisticsService { diff --git a/apps/cs/src/pages/board-statistics/model/chart-data.ts b/apps/cs/src/pages/board-statistics/model/chart-data.ts index 2a9b6671f..487d70976 100644 --- a/apps/cs/src/pages/board-statistics/model/chart-data.ts +++ b/apps/cs/src/pages/board-statistics/model/chart-data.ts @@ -11,20 +11,20 @@ export const chartData = { } => { return { data: { - labels: data.dayList.map((item) => item.date), + labels: data.bbsDailyList.map((item) => item.dayYmd), datasets: [ { type: "line", label: "신규게시물", - data: data.dayList.map((item) => item.posts), + data: data.bbsDailyList.map((item) => item.pstNewCnt), borderColor: getCssVariableValue("--chart-1"), backgroundColor: "rgba(59, 130, 246, 0.1)", tension: 0.4, }, { type: "line", - label: "답변대기", - data: data.dayList.map((item) => item.comments), + label: "댓글수", + data: data.bbsDailyList.map((item) => item.pstCmntCnt), borderColor: getCssVariableValue("--chart-2"), backgroundColor: "rgba(236, 72, 153, 0.1)", tension: 0.4, @@ -32,11 +32,10 @@ export const chartData = { { type: "line", label: "조회수", - data: data.dayList.map((item) => item.views), + data: data.bbsDailyList.map((item) => item.pstInqCnt), borderColor: getCssVariableValue("--chart-3"), backgroundColor: "rgba(168, 85, 247, 0.1)", tension: 0.4, - hidden: true, }, ], }, @@ -67,13 +66,13 @@ export const chartData = { } => { return { data: { - labels: data.boardList.map((item) => item.boardName), + labels: data.bbsPstCntList.map((item) => item.bbsNm), datasets: [ { type: "bar", label: "게시판별 분포", - data: data.boardList.map((item) => item.count), - backgroundColor: data.boardList.map((_, index) => + data: data.bbsPstCntList.map((item) => item.pstCnt), + backgroundColor: data.bbsPstCntList.map((_, index) => getCssVariableValue(`--chart-${(index % 5) + 1}`), ), }, @@ -106,12 +105,12 @@ export const chartData = { } => { return { data: { - labels: data.statusList.map((item) => item.statusName), + labels: data.pstAnsList.map((item) => item.answerNm), datasets: [ { type: "doughnut", - data: data.statusList.map((item) => item.count), - backgroundColor: data.statusList.map((_, index) => + data: data.pstAnsList.map((item) => item.ansCnt), + backgroundColor: data.pstAnsList.map((_, index) => getCssVariableValue(`--chart-${(index % 5) + 1}`), ), }, diff --git a/apps/cs/src/pages/board-statistics/ui/board-statistics-page.tsx b/apps/cs/src/pages/board-statistics/ui/board-statistics-page.tsx index 13dcbe812..716c390ab 100644 --- a/apps/cs/src/pages/board-statistics/ui/board-statistics-page.tsx +++ b/apps/cs/src/pages/board-statistics/ui/board-statistics-page.tsx @@ -26,7 +26,7 @@ import { SelectValue, } from "@common/components"; import { SiteSelect } from "@/entities/site"; -// import { ENDPOINT } from "@/shared/config"; +import { ENDPOINT } from "@/shared/config"; import { NoData } from "@/shared/ui/no-data"; import { InputDateRangePicker } from "@/shared/ui/input-date-range-picker"; import { BoardStatisticsApi } from "../api/board-statistics-service"; @@ -66,7 +66,7 @@ function BoardStatisticsPage() { }); const { data: boardStatisticsData } = useQuery({ - queryKey: ["board-statistics", searchParams], + queryKey: [ENDPOINT.BOARD_SERVICE.STATISTICS, searchParams], queryFn: () => BoardStatisticsApi.getBoardStatistics({ ...searchParams, @@ -176,7 +176,8 @@ function BoardStatisticsPage() {

총 게시물

- {boardStatisticsData?.totalPosts.toLocaleString() || 0} + {boardStatisticsData?.bbsTotalDto?.pstTotCnt?.toLocaleString() || + 0}
답변대기
- {boardStatisticsData?.totalComments.toLocaleString() || 0} + {boardStatisticsData?.bbsTotalDto?.pstAnsWaitCnt?.toLocaleString() || + 0}
평균평점
- {boardStatisticsData?.averageRating.toFixed(1) || 0} + {boardStatisticsData?.bbsTotalDto?.pstEvlAvg?.toFixed(1) || 0}
총 조회수
- {boardStatisticsData?.totalViews.toLocaleString() || 0} + {boardStatisticsData?.bbsTotalDto?.pstInqCnt?.toLocaleString() || + 0}
일별 게시물 현황
- {boardStatisticsData?.dayList?.length ? ( + {boardStatisticsData?.bbsDailyList?.length ? ( ) : ( @@ -329,7 +332,7 @@ function BoardStatisticsPage() {

답변상태

- {boardStatisticsData?.statusList?.length ? ( + {boardStatisticsData?.pstAnsList?.length ? ( ) : ( @@ -342,7 +345,7 @@ function BoardStatisticsPage() {

게시판별 분포

- {boardStatisticsData?.boardList?.length ? ( + {boardStatisticsData?.bbsPstCntList?.length ? ( ) : ( diff --git a/apps/cs/src/pages/code/ui/lwpocode-dialog.tsx b/apps/cs/src/pages/code/ui/lwpocode-dialog.tsx index d21f5b60a..3179303dd 100644 --- a/apps/cs/src/pages/code/ui/lwpocode-dialog.tsx +++ b/apps/cs/src/pages/code/ui/lwpocode-dialog.tsx @@ -1,4 +1,4 @@ -import { memo } from "react"; +import { memo, useEffect } from "react"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { useQuery, useQueryClient } from "@tanstack/react-query"; @@ -140,6 +140,10 @@ const LwpoCodeManagementDialog = memo( } }; + useEffect(() => { + if (!open) form.reset(); + }, [open]); + return ( {children} diff --git a/apps/cs/src/pages/code/ui/upcode-dialog.tsx b/apps/cs/src/pages/code/ui/upcode-dialog.tsx index fafb7429f..6774bd17e 100644 --- a/apps/cs/src/pages/code/ui/upcode-dialog.tsx +++ b/apps/cs/src/pages/code/ui/upcode-dialog.tsx @@ -1,4 +1,4 @@ -import { memo } from "react"; +import { memo, useEffect } from "react"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { useQuery } from "@tanstack/react-query"; @@ -96,6 +96,10 @@ const UpCodeManagementDialog = memo( if (await confirm(confirmMessage)) callback(data); }; + useEffect(() => { + if (!open) form.reset(); + }, [open]); + return ( {children} diff --git a/apps/cs/src/pages/dashboard/api/dashboard-service.ts b/apps/cs/src/pages/dashboard/api/dashboard-service.ts index 94bf807a1..998215864 100644 --- a/apps/cs/src/pages/dashboard/api/dashboard-service.ts +++ b/apps/cs/src/pages/dashboard/api/dashboard-service.ts @@ -1,6 +1,13 @@ import { client } from "@/shared/api"; import { ENDPOINT } from "@/shared/config"; -import type { Dashboard, DashboardService } from "../model/dashboard-interface"; +import type { + Dashboard, + DashboardService, + NoticeUser, + Schedule, + ScheduleQueryParams, +} from "../model/dashboard-interface"; +import type { PageableList } from "@/shared/model"; export class DashboardApiService implements DashboardService { public async getDashboard(): Promise { @@ -9,6 +16,25 @@ export class DashboardApiService implements DashboardService { ); return response.data; } + + public async getNoticeUserList( + recvUnqId: string, + ): Promise> { + const response = await client.get>( + `${ENDPOINT.CMS_SERVICE.NOTICE_USER}/${recvUnqId}/list`, + ); + return response.data; + } + + public async getScheduleList( + params?: ScheduleQueryParams, + ): Promise { + const response = await client.get( + ENDPOINT.CMS_SERVICE.SCHEDULES, + { params }, + ); + return response.data; + } } export const DashboardApi = new DashboardApiService(); diff --git a/apps/cs/src/pages/dashboard/model/dashboard-interface.ts b/apps/cs/src/pages/dashboard/model/dashboard-interface.ts index 9e038eef7..e2a01173d 100644 --- a/apps/cs/src/pages/dashboard/model/dashboard-interface.ts +++ b/apps/cs/src/pages/dashboard/model/dashboard-interface.ts @@ -1,3 +1,5 @@ +import type { PageableList } from "@/shared/model"; + export interface SiteStatisticsContent { siteId: string; siteNm: string; @@ -27,6 +29,42 @@ export interface Dashboard { menuAccessList: MenuStatisticsContent[]; } +export interface NoticeUser { + notiId: number; + recvUnqId: string; + senderUnqId: string; + notiType: string; + notiTtl: string; + notReadYn: string; + regDt: string; +} + +export interface Schedule { + schdlBgngDt: string; + schdlEndDt: string; + actualDate: string; + schdlSn: number; + siteId: string; + schdlTtl: string; + schdlSeCd: string; + schdlCn: string; + reptYn: string; + commSchdlYn: string; + atflCd: string | null; +} + +export interface ScheduleQueryParams { + siteId?: string; + schdlTtl?: string; + schdlSeCd?: string; + schdlBgngYmd?: string; + schdlEndYmd?: string; + reptYn?: string; + commSchdlYn?: string; +} + export interface DashboardService { getDashboard(): Promise; + getNoticeUserList(recvUnqId: string): Promise>; + getScheduleList(params?: ScheduleQueryParams): Promise; } diff --git a/apps/cs/src/pages/dashboard/ui/dashboard-page.tsx b/apps/cs/src/pages/dashboard/ui/dashboard-page.tsx index 65fd0030a..d787a6ab7 100644 --- a/apps/cs/src/pages/dashboard/ui/dashboard-page.tsx +++ b/apps/cs/src/pages/dashboard/ui/dashboard-page.tsx @@ -1,5 +1,6 @@ "use client"; +import dayjs from "dayjs"; import { useRouter } from "next/navigation"; import { useQuery } from "@tanstack/react-query"; import { getCoreRowModel } from "@tanstack/react-table"; @@ -17,8 +18,8 @@ import { } from "chart.js"; import { Bar, Line } from "react-chartjs-2"; import { PostApi, type RecentPost } from "@/entities/post"; -import { ENDPOINT } from "@/shared/config"; -import { useDataTable } from "@/shared/lib"; +import { AUTH_USER_ID, ENDPOINT, SITE_ID } from "@/shared/config"; +import { getCookie, useDataTable } from "@/shared/lib"; import { DashboardApi } from "../api/dashboard-service"; import { chartData } from "../model/chart-data"; import { @@ -39,12 +40,44 @@ ChartJS.register( ); function DashboardPage() { + const userId = getCookie(AUTH_USER_ID) ?? ""; + const router = useRouter(); + const { data } = useQuery({ queryKey: [ENDPOINT.CMS_SERVICE.DASHBOARD], queryFn: () => DashboardApi.getDashboard(), }); + const { data: noticeUserList } = useQuery({ + queryKey: [ENDPOINT.CMS_SERVICE.NOTICE_USER], + queryFn: () => DashboardApi.getNoticeUserList(userId), + enabled: !!userId, + select: (data) => { + // 오늘 ~ 한 달 전 사이의 일정 + const today = dayjs().endOf("day"); + const monthAgo = dayjs().subtract(1, "month").startOf("day"); + return data.content?.filter((item) => { + const regDt = dayjs(item.regDt); + return ( + (regDt.isSame(monthAgo) || regDt.isAfter(monthAgo)) && + (regDt.isSame(today) || regDt.isBefore(today)) + ); + }); + }, + }); + + const { data: scheduleList } = useQuery({ + queryKey: [ENDPOINT.CMS_SERVICE.SCHEDULES], + queryFn: () => + DashboardApi.getScheduleList({ + siteId: SITE_ID, + schdlBgngYmd: dayjs().format("YYYY-MM-DD"), + schdlEndYmd: dayjs().add(1, "month").format("YYYY-MM-DD"), + commSchdlYn: "Y", + }), + }); + const noticePostQuery = useQuery({ queryKey: [ENDPOINT.BOARD_SERVICE.POSTS], queryFn: () => @@ -323,22 +356,15 @@ function DashboardPage() {
-
- 신규 사용자 가입 - {(1).toLocaleString()}분 전 -
-
- 메뉴 수정 요청 - {(5).toLocaleString()}분 전 -
-
- 게시물 신고 - {(10).toLocaleString()}분 전 -
-
- 컨텐츠 승인 요청 - {(15).toLocaleString()}분 전 -
+ {noticeUserList?.map((item) => ( +
+ {item.notiTtl} + {dayjs(item.regDt).fromNow()} +
+ ))}
@@ -349,24 +375,44 @@ function DashboardPage() {
-
- - {(3).toLocaleString()}월 정기 시스템 점검 - - 내일 -
-
- 서버 업데이트 예정 - {(3).toLocaleString()}일 후 -
-
- 백업 스케줄 - {(5).toLocaleString()}일 후 -
-
- 정기 보안 점검 - {(7).toLocaleString()}일 후 -
+ {scheduleList && scheduleList.length > 0 ? ( + scheduleList.map((schedule, index) => { + const today = dayjs(); + const scheduleDate = dayjs(schedule.actualDate); + const daysUntil = scheduleDate.diff(today, "day"); + + let timeText = ""; + if (daysUntil === 0) { + timeText = "오늘"; + } else if (daysUntil === 1) { + timeText = "내일"; + } else if (daysUntil > 1) { + timeText = `${daysUntil}일 후`; + } else { + timeText = scheduleDate.format("YYYY-MM-DD"); + } + + return ( +
+ + {schedule.schdlTtl} + + {timeText} +
+ ); + }) + ) : ( +
+ 예정된 일정이 없습니다. +
+ )}
diff --git a/apps/cs/src/pages/groups/ui/menu-authority-dialog.tsx b/apps/cs/src/pages/groups/ui/menu-authority-dialog.tsx index 38c6597fc..cde3cf641 100644 --- a/apps/cs/src/pages/groups/ui/menu-authority-dialog.tsx +++ b/apps/cs/src/pages/groups/ui/menu-authority-dialog.tsx @@ -1,4 +1,4 @@ -import { useState, useRef } from "react"; +import { useState, useRef, useEffect } from "react"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { useQuery } from "@tanstack/react-query"; @@ -48,25 +48,14 @@ function MenuAuthorityDialog({ groupId }: MenuAuthorityDialogProps) { keywordType: "", siteId: "", }); - - const handleReset = () => { - if (inputRef.current) inputRef.current.value = ""; - setKeywordType(""); - setSiteId(""); - setSearchParams({ - keyword: "", - keywordType: "", - siteId: "", - }); - }; - const query = useQuery({ queryKey: [ENDPOINT.CMS_SERVICE.MENU_AUTHRTS, groupId, searchParams], - queryFn: () => MenuAuthorityApi.getGroupMenuAuthorityList({ - groupId, - ...searchParams - }), + queryFn: () => + MenuAuthorityApi.getGroupMenuAuthorityList({ + groupId, + ...searchParams, + }), enabled: open && !!groupId, }); @@ -117,6 +106,21 @@ function MenuAuthorityDialog({ groupId }: MenuAuthorityDialogProps) { }); }; + const handleReset = () => { + if (inputRef.current) inputRef.current.value = ""; + setSiteId(""); + setKeywordType(""); + setSearchParams({ + keyword: "", + keywordType: "", + siteId: "", + }); + }; + + useEffect(() => { + if (!open) handleReset(); + }, [open]); + return ( diff --git a/apps/cs/src/pages/module/ui/management-authority-dialog.tsx b/apps/cs/src/pages/module/ui/management-authority-dialog.tsx index 0459ba804..b3e6f3fe5 100644 --- a/apps/cs/src/pages/module/ui/management-authority-dialog.tsx +++ b/apps/cs/src/pages/module/ui/management-authority-dialog.tsx @@ -1,4 +1,4 @@ -import { useState, memo } from "react"; +import { useState, memo, useEffect } from "react"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { useQuery } from "@tanstack/react-query"; @@ -90,6 +90,10 @@ const ManagementAuthorityDialog = memo( } }; + useEffect(() => { + if (!open) form.reset(); + }, [open]); + return ( diff --git a/apps/cs/src/pages/role/ui/mapping-role-dialog.tsx b/apps/cs/src/pages/role/ui/mapping-role-dialog.tsx index 5377f6a7b..e327fb88e 100644 --- a/apps/cs/src/pages/role/ui/mapping-role-dialog.tsx +++ b/apps/cs/src/pages/role/ui/mapping-role-dialog.tsx @@ -1,4 +1,4 @@ -import { useState, useRef } from "react"; +import { useState, useRef, useEffect } from "react"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { getCoreRowModel, type ColumnDef } from "@tanstack/react-table"; import { @@ -113,6 +113,10 @@ function MappingRoleDialog({ }, }); + useEffect(() => { + if (!open) handleReset(); + }, [open]); + return ( diff --git a/apps/cs/src/shared/config/api.ts b/apps/cs/src/shared/config/api.ts index ce6f61381..db8eda72c 100644 --- a/apps/cs/src/shared/config/api.ts +++ b/apps/cs/src/shared/config/api.ts @@ -56,6 +56,7 @@ export const ENDPOINT = { APILOG: "/cms-service/api/v1/api-logs", SITE_AUTHORITY: "/cms-service/api/v1/site-authrt", CONNECTION_LOG: "/cms-service/api/v1/cntnlog", + NOTICE_USER: "/cms-service/api/v1/noti/user", }, BOARD_SERVICE: { BBS: "/board-service/api/v1/bbs", @@ -63,6 +64,7 @@ export const ENDPOINT = { POSTS: "/board-service/api/v1/posts", POSTS_COMMENTS: "/board-service/api/v1/posts-cmnts", POST_EVALUATION: "/board-service/api/v1/pstEvl", + STATISTICS: "/board-service/api/v1/stat/bbs", }, }; diff --git a/apps/dm/.env.local b/apps/dm/.env.local index 44e7072c3..88b6f8067 100644 --- a/apps/dm/.env.local +++ b/apps/dm/.env.local @@ -4,20 +4,19 @@ 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://con.mind-one.co.kr:11035 NEXT_PUBLIC_PROXY_HOST = http://localhost:3005 -NEXT_PUBLIC_DM_SERVER_API_URL = http://con.mind-one.co.kr:11083/reuse-service/api/v1 -NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000 -NEXT_PUBLIC_FO_SERVER_API_URL = http://con.mind-one.co.kr:11083/operation-service/api/v1 -# NEXT_PUBLIC_FO_SERVER_API_URL = http://192.168.1.222:8883/operation-service/api/v1 NEXT_PUBLIC_CS_SERVER_API_URL = http://dev.onasys.co.kr:8000 +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://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 +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_WR_SERVER_API_URL = http://con.mind-one.co.kr:11083/reuse-service/api/v1 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 = http://dev.onasys.co.kr:3000/auth/login diff --git a/apps/dm/.env.mindone b/apps/dm/.env.mindone index 1ca3ecfb6..da6f80177 100644 --- a/apps/dm/.env.mindone +++ b/apps/dm/.env.mindone @@ -14,6 +14,7 @@ NEXT_PUBLIC_WR_SERVER_API_URL = http://con.mind-one.co.kr:11083/reuse-service/ap 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 = http://dev.onasys.co.kr:3000/auth/login diff --git a/apps/dm/.env.production b/apps/dm/.env.production index 5aeaa9c45..5a401c89a 100644 --- a/apps/dm/.env.production +++ b/apps/dm/.env.production @@ -15,7 +15,8 @@ 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_SEWERAGE_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/sewerage-service -NEXT_PUBLIC_NGII_URL = http://172.29.143.87 +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} diff --git a/apps/dm/app/(base-layout)/pages/sdmMng/sdmInfoInsert/modalSimilar/page.tsx b/apps/dm/app/(base-layout)/pages/sdmMng/sdmInfoInsert/modalSimilar/page.tsx index 22418c7b7..35836d07c 100644 --- a/apps/dm/app/(base-layout)/pages/sdmMng/sdmInfoInsert/modalSimilar/page.tsx +++ b/apps/dm/app/(base-layout)/pages/sdmMng/sdmInfoInsert/modalSimilar/page.tsx @@ -87,9 +87,9 @@ export default function ModalSimilarPage({ sply = 0, usageCd = '', money = 0 }: }, [getSimilarCaseList, money, searchUseType, sply, usageCd]); return ( -
+
- <div className="overflow-x-auto h-[450px] w-full mt-8" tabIndex={0} aria-label="유사사례 테이블 영역"> + <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> @@ -148,7 +148,7 @@ export default function ModalSimilarPage({ sply = 0, usageCd = '', money = 0 }: </div> </div> </div> - <div className="overflow-x-auto h-[270px] w-full mt-8"> + <div className="overflow-x-auto h-[270px] w-full mt-8 overflow-y-hidden"> <div className="w-[130%]"> <UiTable headName={"사업비 상세정보"} diff --git a/apps/dm/app/(base-layout)/pages/sdmMng/sdmInfoInsert/page.tsx b/apps/dm/app/(base-layout)/pages/sdmMng/sdmInfoInsert/page.tsx index b6a395974..df418b20f 100644 --- a/apps/dm/app/(base-layout)/pages/sdmMng/sdmInfoInsert/page.tsx +++ b/apps/dm/app/(base-layout)/pages/sdmMng/sdmInfoInsert/page.tsx @@ -1,5 +1,11 @@ "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"; @@ -60,7 +66,7 @@ function SdmInfoInsertContent() { ...prev, sdmNm: searchParams.get("dmdCoNm") || "", })); - if(searchParams.get("dmdInfoId")){ + if (searchParams.get("dmdInfoId")) { // const data = { // dmdCoNm: searchParams.get("dmdCoNm") || '', // ctpvCd: searchParams.get("ctpvCd") || '', @@ -69,8 +75,8 @@ function SdmInfoInsertContent() { // daddr: searchParams.get("daddr") || '', // telno: searchParams.get("telno") || '', // } - - // sessionStorage.setItem("user", JSON.stringify(data || {})); + + // sessionStorage.setItem("user", JSON.stringify(data || {})); } //sessionStorage.setItem("user", JSON.parse(user||'') || ""); setIsLogin(user || searchParams.get("dmdInfoId") ? true : false); @@ -121,7 +127,7 @@ function SdmInfoInsertContent() { const userData = JSON.parse(user); if (userData.dmdCoNm) { setFacilityName(userData.dmdCoNm); - form.setValue("sdmNm", userData.dmdCoNm); + form.setValue("sdmNm", userData.dmdCoNm); // 수요처명(회사명) } } catch (error) { console.error("Error parsing user data:", error); @@ -203,10 +209,10 @@ function SdmInfoInsertContent() { tph: Number(data.tph) || null, trbt: Number(data.trbt) || null, ph: data.ph || "", - blgcOxygDmndQnt: Number(data.bod) ||null, + blgcOxygDmndQnt: Number(data.bod) || null, clIonQnt: Number(data.clIonQnt) || null, smll: data.smll || "", - cdtnv: Number(data.cdtnv) ||null, + cdtnv: Number(data.cdtnv) || null, crmty: Number(data.crmty) || null, rmrk: data.rmrk || "", bzmnNo: "", diff --git a/apps/dm/app/(base-layout)/pages/sdmMng/sdmLogout/page.tsx b/apps/dm/app/(base-layout)/pages/sdmMng/sdmLogout/page.tsx new file mode 100644 index 000000000..2c682bdbf --- /dev/null +++ b/apps/dm/app/(base-layout)/pages/sdmMng/sdmLogout/page.tsx @@ -0,0 +1,22 @@ +"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; +} diff --git a/apps/dm/app/layout.tsx b/apps/dm/app/layout.tsx index 462537e09..7224c9dc2 100644 --- a/apps/dm/app/layout.tsx +++ b/apps/dm/app/layout.tsx @@ -1,5 +1,8 @@ "use client"; import "./globals.css"; +import { setCookie } from "../src/shared/lib"; + +setCookie("site-id-key", process.env.NEXT_PUBLIC_SITE_ID || "DM"); export default function RootLayout({ children, diff --git a/apps/dm/app/store/sdmMng/index.tsx b/apps/dm/app/store/sdmMng/index.tsx index b5b258839..2e52ac13d 100644 --- a/apps/dm/app/store/sdmMng/index.tsx +++ b/apps/dm/app/store/sdmMng/index.tsx @@ -50,8 +50,8 @@ export const sdmInfoFormSchema = z.object({ // 모든 수질 관련 필드 // 총대장균군수는 null 값을 불검출로 표시하기때문에 해당 검사에서 제외함. const allFields: (keyof typeof data)[] = [ - "tn", "combResdch", "tph", - "trbt", "ph", "bod", "clIonQnt", + "tn", "combResdch", "tph", + "trbt", "ph", "bod", "clIonQnt", "smll", "cdtnv", "crmty", ]; @@ -104,38 +104,38 @@ export type SdmInfoFormData = z.infer<typeof sdmInfoFormSchema>; // 수요처 정보 export type DemandCompBizType = { - index : number | ''; - dmdInfoId : string | ''; - dmdInfoFk : string | ''; - bzmnNo : string | ''; - dmdCoNm : string | ''; - daddr : string | ''; - telno : string | ''; - rgtr : string | ''; - useUsgCd : string | ''; - ctpvCd : string | ''; - sggCd : string | ''; - dmdAplyPswd : string | ''; - wntUntprc : number | ''; - dmndTmng : Date | ''; - dmndQty : number | ''; - grssClfg : number | null //총대장균군수 - tn : number | null //총질소 - combResdch : number | null //결합잔류염소 - tph : number | null //총인 - trbt : number | null //탁도 - ph : string | ''; //수소이온농 - blgcOxygDmndQnt : number | null //생물학적산소요구량 - clIonQnt : number | null //염화물 - smll : string | ''; //냄새 - cdtnv : number | null; //전기전도도 - crmty : number | null //색도 - rmrk : string | ''; - reqDt : string | ''; - dmdInfoSn : number | ''; - status : string | ''; - splyCndtList:SupplyCandidate[]|[], - ruseUtztnFcltNm?:string|'' + index: number | ''; + dmdInfoId: string | ''; + dmdInfoFk: string | ''; + bzmnNo: string | ''; + dmdCoNm: string | ''; + daddr: string | ''; + telno: string | ''; + rgtr: string | ''; + useUsgCd: string | ''; + ctpvCd: string | ''; + sggCd: string | ''; + dmdAplyPswd: string | ''; + wntUntprc: number | ''; + dmndTmng: Date | ''; + dmndQty: number | ''; + grssClfg: number | null //총대장균군수 + tn: number | null //총질소 + combResdch: number | null //결합잔류염소 + tph: number | null //총인 + trbt: number | null //탁도 + ph: string | ''; //수소이온농 + blgcOxygDmndQnt: number | null //생물학적산소요구량 + clIonQnt: number | null //염화물 + smll: string | ''; //냄새 + cdtnv: number | null; //전기전도도 + crmty: number | null //색도 + rmrk: string | ''; + reqDt: string | ''; + dmdInfoSn: number | ''; + status: string | ''; + splyCndtList: SupplyCandidate[] | [], + ruseUtztnFcltNm?: string | '' } @@ -147,107 +147,107 @@ export type SupplyCandidate = { export type DemandMatchings = { - splyFcltFk : string | ''; - splyFcltFlg : string | ''; - pbnstRuseFcltBizFk : number | ''; - splyBgngDay : string | ''; - splyEndDay : string | ''; - splyCpct : number | ''; - splyFmYn : string | ''; - useUsgCd : string | ''; - dmdInfoSn : number | ''; - splyCndtList : string | ''; - mtchnCmptnYmd : string | ''; - rvwCmptnYmd : string | ''; - regDt : string | ''; - rgtr : string | ''; - mdfcnDt : string | ''; - mdfr : string | ''; + splyFcltFk: string | ''; + splyFcltFlg: string | ''; + pbnstRuseFcltBizFk: number | ''; + splyBgngDay: string | ''; + splyEndDay: string | ''; + splyCpct: number | ''; + splyFmYn: string | ''; + useUsgCd: string | ''; + dmdInfoSn: number | ''; + splyCndtList: string | ''; + mtchnCmptnYmd: string | ''; + rvwCmptnYmd: string | ''; + regDt: string | ''; + rgtr: string | ''; + mdfcnDt: string | ''; + mdfr: string | ''; } // 유사사례 export type SimilarCaseType = { - ruseBizNm : string | ''; - bizBgngDate : string | ''; - bizEndDate : string | ''; - splyFcltKnd : string | ''; - splyFcltNm : string | ''; - splyFcltCpct : string | ''; - dmdFcltNm : string | ''; - dmdFcltAddr : string | ''; - ruseFcltCpct : string | ''; - ruseFcltPplLen : string | ''; - ruseFcltUsg : string | ''; - ruseFcltSplyCpct : string | ''; - ruseFcltPrcsMsrc : string | ''; - grnteWqUsg : string | ''; - grnteWqGrssClfg : string | ''; - grnteWqCombResdch : string | ''; - grnteWqTrbt : string | ''; - grnteWqBod : string | ''; - grnteWqSmll : string | ''; - grnteWqCrmty : string | ''; - grnteWqTn : string | ''; - grnteWqTp : string | ''; - grnteWqPh : string | ''; - grnteWqClIonQnt : string | ''; - grnteWqCndctStat : string | ''; - grnteWqRmrk : string | ''; - reqBizReuseFcltCst : string | ''; - reqBizPplCst : string | ''; - reqBizArchCst : string | ''; - reqBizMchnCst : string | ''; - reqBizMtrngCst : string | ''; - reqBizGisCst : string | ''; - reqBizCctvCst : string | ''; - reqBizCmcnCst : string | ''; - reqBizWstCst : string | ''; - reqBizDesignCst : string | ''; - reqBizSpvsCst : string | ''; - reqBizSbsdCst : string | ''; - reqBizEtc : string | ''; - reqBizGrssCst : string | ''; - prcmPlanNlep : string | ''; - prcmPlanLcep : string | ''; - prcmPlanBrdnCst : string | ''; - prcmPlanEcqrRt : string | ''; - prcmPlanGrssCst : string | ''; - clclnNlep : string | ''; - clclnLcep : string | ''; - clclnBrdnCst : string | ''; - clclnEcqrRt : string | ''; - clclnGrssCst : string | ''; + ruseBizNm: string | ''; // 사업명 + bizBgngDate: string | ''; + bizEndDate: string | ''; + splyFcltKnd: string | ''; + splyFcltNm: string | ''; + splyFcltCpct: string | ''; + dmdFcltNm: string | ''; + dmdFcltAddr: string | ''; + ruseFcltCpct: string | ''; + ruseFcltPplLen: string | ''; + ruseFcltUsg: string | ''; + ruseFcltSplyCpct: string | ''; + ruseFcltPrcsMsrc: string | ''; + grnteWqUsg: string | ''; + grnteWqGrssClfg: string | ''; + grnteWqCombResdch: string | ''; + grnteWqTrbt: string | ''; + grnteWqBod: string | ''; + grnteWqSmll: string | ''; + grnteWqCrmty: string | ''; + grnteWqTn: string | ''; + grnteWqTp: string | ''; + grnteWqPh: string | ''; + grnteWqClIonQnt: string | ''; + grnteWqCndctStat: string | ''; + grnteWqRmrk: string | ''; + reqBizReuseFcltCst: string | ''; + reqBizPplCst: string | ''; + reqBizArchCst: string | ''; + reqBizMchnCst: string | ''; + reqBizMtrngCst: string | ''; + reqBizGisCst: string | ''; + reqBizCctvCst: string | ''; + reqBizCmcnCst: string | ''; + reqBizWstCst: string | ''; + reqBizDesignCst: string | ''; + reqBizSpvsCst: string | ''; + reqBizSbsdCst: string | ''; + reqBizEtc: string | ''; + reqBizGrssCst: string | ''; + prcmPlanNlep: string | ''; + prcmPlanLcep: string | ''; + prcmPlanBrdnCst: string | ''; + prcmPlanEcqrRt: string | ''; + prcmPlanGrssCst: string | ''; + clclnNlep: string | ''; + clclnLcep: string | ''; + clclnBrdnCst: string | ''; + clclnEcqrRt: string | ''; + clclnGrssCst: string | ''; } // 사업비 상세정보 export type MoneyDetailType = { - reqBizGrssCst : string | ''; - reqBizReuseFcltCst : string | ''; - reqBizPplCst : string | ''; - reqBizArchCst : string | ''; - reqBizMchnCst : string | ''; - reqBizMtrngCst : string | ''; - reqBizGisCst : string | ''; - reqBizCctvCst : string | ''; - reqBizCmcnCst : string | ''; - reqBizWstCst : string | ''; - reqBizDesignCst : string | ''; - reqBizSpvsCst : string | ''; - reqBizSbsdCst : string | ''; - reqBizEtc : string | ''; - + reqBizGrssCst: string | ''; + reqBizReuseFcltCst: string | ''; + reqBizPplCst: string | ''; + reqBizArchCst: string | ''; + reqBizMchnCst: string | ''; + reqBizMtrngCst: string | ''; + reqBizGisCst: string | ''; + reqBizCctvCst: string | ''; + reqBizCmcnCst: string | ''; + reqBizWstCst: string | ''; + reqBizDesignCst: string | ''; + reqBizSpvsCst: string | ''; + reqBizSbsdCst: string | ''; + reqBizEtc: string | ''; + /** 재원조달계획(백만원원) */ - prcmPlanGrssCst : string | ''; - prcmPlanNlep : string | ''; - prcmPlanLcep : string | ''; - prcmPlanBrdnCst : string | ''; - prcmPlanEcqrRt : string | ''; + prcmPlanGrssCst: string | ''; + prcmPlanNlep: string | ''; + prcmPlanLcep: string | ''; + prcmPlanBrdnCst: string | ''; + prcmPlanEcqrRt: string | ''; /** 사업비 정산(백만원) */ - clclnGrssCst : string | ''; - clclnNlep : string | ''; - clclnLcep : string | ''; - clclnBrdnCst : string | ''; - clclnEcqrRt : string | ''; + clclnGrssCst: string | ''; + clclnNlep: string | ''; + clclnLcep: string | ''; + clclnBrdnCst: string | ''; + clclnEcqrRt: string | ''; } // 규칙 타입 선언 export type DisableRulesType = Record<string, string[]>; @@ -255,144 +255,144 @@ export type DisableRulesType = Record<string, string[]>; * Store - 데이터 ====================================*/ // 수요처 정보 -const demandCompList : DemandCompType[] = [ +const demandCompList: DemandCompType[] = [ ] const disableRules: DisableRulesType = { - "청소": ["tn", 'tph',"clIonQnt" ,'cdtnv'], - "화장실용수": ["tn", 'tph',"clIonQnt" ,'cdtnv'], - "세척": ["combResdch", "crmty",'tph','tn',"clIonQnt",'cdtnv'], - "살수": ["combResdch", "crmty",'tph','tn',"clIonQnt",'cdtnv'], - "조경": ["combResdch", "crmty",'tph','cdtnv'], - "농업_간접용수": ["combResdch", "crmty",'tn','tph','clIonQnt'], - "농업_직접용수": ["combResdch", "crmty",'tn','tph','clIonQnt'], - "하천": ["combResdch", "trbt", "crmty" ,'clIonQnt','cdtnv'], - '공업':[] - }; + "청소": ["tn", 'tph', "clIonQnt", 'cdtnv'], + "화장실용수": ["tn", 'tph', "clIonQnt", 'cdtnv'], + "세척": ["combResdch", "crmty", 'tph', 'tn', "clIonQnt", 'cdtnv'], + "살수": ["combResdch", "crmty", 'tph', 'tn', "clIonQnt", 'cdtnv'], + "조경": ["combResdch", "crmty", 'tph', 'cdtnv'], + "농업_간접용수": ["combResdch", "crmty", 'tn', 'tph', 'clIonQnt'], + "농업_직접용수": ["combResdch", "crmty", 'tn', 'tph', 'clIonQnt'], + "하천": ["combResdch", "trbt", "crmty", 'clIonQnt', 'cdtnv'], + '공업': [] +}; - const defaultRules: Record<string, object> = { - "청소": { - grssClfg: null, - combResdch:0.2, - trbt: 2, - bod: 5, - crmty: 20, - ph: "5.8~8.5", - smll:'불쾌하지않을것', - tn:null, - tph: null, - clIonQnt: null, - cdtnv: null, +const defaultRules: Record<string, object> = { + "청소": { + grssClfg: null, + combResdch: 0.2, + trbt: 2, + bod: 5, + crmty: 20, + ph: "5.8~8.5", + smll: '불쾌하지않을것', + tn: null, + tph: null, + clIonQnt: null, + cdtnv: null, - }, - "화장실용수": { - grssClfg: null, - combResdch:0.2, - trbt: 2, - bod: 5, - crmty: 20, - smll:'불쾌하지않을것', - ph: "5.8~8.5", - tn:null, - tph: null, - clIonQnt: null, - cdtnv: null, - }, - "세척": { - grssClfg: null, - trbt: 2, - bod: 5, - ph: "5.8~8.5", - combResdch:null, - smll:'불쾌하지않을것', - crmty:null, - tn:null, - tph: null, - clIonQnt: null, - cdtnv: null, - }, - "살수": { - grssClfg: null, - trbt: 2, - bod: 5, - ph: "5.8~8.5", - smll:'불쾌하지않을것', - combResdch:null, - crmty:null, - tn:null, - tph: null, - clIonQnt: null, - cdtnv: null, - }, - "조경": { - grssClfg: null, - trbt: 2, - bod: 5, - ph: "5.8~8.5", - smll:'불쾌하지않을것', - clIonQnt:250, - combResdch:null, - crmty:null, - tn:null, - tph: null, - cdtnv: null, - }, - "농업_간접용수": { - grssClfg: null, - trbt: 5, - bod: 8, - ph: "5.8~8.5", - cdtnv:2000, - combResdch:null, - crmty:null, - tn:null, - tph: null, - clIonQnt: null, - }, - "농업_직접용수": { - grssClfg: null, - trbt: 2, - bod: 8, - smll:'불쾌하지않을것', - ph: "5.8~8.5", - cdtnv:700, - combResdch:null, - crmty:null, - tn:null, - tph: null, - clIonQnt: null, - }, - "하천": { - grssClfg: 1000, - bod: 5, - tn: 10, - tph: 0.5, - smll:'불쾌하지않을것', - ph: "5.8~8.5", - combResdch:null, - trbt:null, - crmty:null, - clIonQnt: null, - cdtnv:null - }, - '공업':{ - grssClfg: null, - bod: null, - tn: null, - tph: null, - ph: "~", - combResdch:null, - smll:'불쾌하지않을것', - trbt:null, - crmty:null, - clIonQnt: null, - cdtnv:null - } - }; + }, + "화장실용수": { + grssClfg: null, + combResdch: 0.2, + trbt: 2, + bod: 5, + crmty: 20, + smll: '불쾌하지않을것', + ph: "5.8~8.5", + tn: null, + tph: null, + clIonQnt: null, + cdtnv: null, + }, + "세척": { + grssClfg: null, + trbt: 2, + bod: 5, + ph: "5.8~8.5", + combResdch: null, + smll: '불쾌하지않을것', + crmty: null, + tn: null, + tph: null, + clIonQnt: null, + cdtnv: null, + }, + "살수": { + grssClfg: null, + trbt: 2, + bod: 5, + ph: "5.8~8.5", + smll: '불쾌하지않을것', + combResdch: null, + crmty: null, + tn: null, + tph: null, + clIonQnt: null, + cdtnv: null, + }, + "조경": { + grssClfg: null, + trbt: 2, + bod: 5, + ph: "5.8~8.5", + smll: '불쾌하지않을것', + clIonQnt: 250, + combResdch: null, + crmty: null, + tn: null, + tph: null, + cdtnv: null, + }, + "농업_간접용수": { + grssClfg: null, + trbt: 5, + bod: 8, + ph: "5.8~8.5", + cdtnv: 2000, + combResdch: null, + crmty: null, + tn: null, + tph: null, + clIonQnt: null, + }, + "농업_직접용수": { + grssClfg: null, + trbt: 2, + bod: 8, + smll: '불쾌하지않을것', + ph: "5.8~8.5", + cdtnv: 700, + combResdch: null, + crmty: null, + tn: null, + tph: null, + clIonQnt: null, + }, + "하천": { + grssClfg: 1000, + bod: 5, + tn: 10, + tph: 0.5, + smll: '불쾌하지않을것', + ph: "5.8~8.5", + combResdch: null, + trbt: null, + crmty: null, + clIonQnt: null, + cdtnv: null + }, + '공업': { + grssClfg: null, + bod: null, + tn: null, + tph: null, + ph: "~", + combResdch: null, + smll: '불쾌하지않을것', + trbt: null, + crmty: null, + clIonQnt: null, + cdtnv: null + } +}; // 사용용도 -const useTypesData : SelectDataType[] = [ +const useTypesData: SelectDataType[] = [ { text: '청소', val: "청소" }, { text: "화장실용수", val: "화장실용수" }, { text: "세척", val: "세척" }, @@ -440,8 +440,8 @@ export const similarCaseModalHeader: TableUpperProps[] = [ ] // 유사사례 데이터 -const similarCaseList : SimilarCaseType[] = [ - +const similarCaseList: SimilarCaseType[] = [ + ] // 사업비 상세정보 모달 헤더 @@ -477,12 +477,12 @@ export const moneyDetailModalHeader: TableUpperProps[] = [ ] // 사업비 상세정보 -const moneyDetailList : MoneyDetailType[] = [ +const moneyDetailList: MoneyDetailType[] = [ ] // 수요정보 신청현황 헤더 export const upHeadSdmInfoStatus: TableUpperProps[] = [ - { id: "index", title: "No."}, + { id: "index", title: "No." }, { id: "ctpvCd", title: "시도", row: 0, rowSpan: 2 }, { id: "sggCd", title: "시군구", row: 0, rowSpan: 2 }, { id: "dmdCoNm", title: "회사명", row: 0, rowSpan: 2 }, @@ -501,7 +501,7 @@ export const upHeadSdmInfoStatus: TableUpperProps[] = [ const postSdmData = async (data: DemandCompType) => { try { await postFetch("/reuseFc/request", data); - sessionStorage.setItem("user", JSON.stringify(data) ); + sessionStorage.setItem("user", JSON.stringify(data)); toast.success("수요처 등록에 성공하였습니다."); return true; } catch (error) { @@ -526,16 +526,16 @@ const postSdmLoginData = async (data: DemandCompType) => { try { const res = await postFetch("/reuseFc/request/login", data); const jsonData = await res.json(); - + const status = jsonData.status; const loginData = jsonData.data; let loginSuccess = false; - if(status === 200 && loginData != null) { + if (status === 200 && loginData != null) { alert("수요처 로그인에 성공하였습니다."); sessionStorage.setItem("user", JSON.stringify(loginData)); loginSuccess = true; - } else if(status === 401) { - alert("패스워드가 틀렸습니다."); + } else if (status === 401) { + alert("패스워드가 일치하지 않습니다."); loginSuccess = false; } else { alert("등록된 수요처가 없습니다."); @@ -553,35 +553,35 @@ const getDemandCompList = async (searchText: string, pageCnt: number) => { try { const url = '/reuseFc/request/' + (searchText ? searchText : '') + '/' + pageCnt; const res = await getFetch(fetchType.WR, url); - return { - data: res?.data || [], - count: res?.count || 0 + return { + data: res?.data || [], + count: res?.count || 0 } } catch (error) { - console.error(error+' 수요처 정보를 불러오지 못했습니다.'); + console.error(error + ' 수요처 정보를 불러오지 못했습니다.'); return { data: [], count: 0 }; } } -const getSdmInfoStatusList = async (dmdInfoId : string) => { +const getSdmInfoStatusList = async (dmdInfoId: string) => { try { const url = '/reuseFc/request/' + dmdInfoId; const res = await getFetch(fetchType.WR, url); return res?.data || []; } catch (error) { - console.error(error+' 수요정보 신청현황을 불러오지 못했습니다.'); + console.error(error + ' 수요정보 신청현황을 불러오지 못했습니다.'); return []; } } // 유사사례 조회 -const fetchGetSimilarCaseList = async (part: string, value: number|string) => { +const fetchGetSimilarCaseList = async (part: string, value: number | string) => { try { const url = '/reuseFc/pubFcRuseCase/dm/' + part + '/' + (value ? value : 0); const res = await getFetch(fetchType.WR, url); return res?.data || []; } catch (error) { - console.error(error+' 유사사례 조회에 실패하였습니다.'); + console.error(error + ' 유사사례 조회에 실패하였습니다.'); return []; } } @@ -590,23 +590,24 @@ const fetchGetSimilarCaseList = async (part: string, value: number|string) => { * Store - expaort ====================================*/ export type SdmActions = { - initializeSdmData: () => void; - insertSdmData: (data: DemandCompType) => Promise<void>; - insertSdmInfoData: (data: DemandCompBizType) => Promise<void>; - getDemandCompList: (searchText: string, pageCnt: number) => Promise<void>; - actionLogin: (data: DemandCompType) => Promise<void>; - getSdmInfoStatusList: (dmdInfoId: string) => Promise<void>; - getSimilarCaseList: (part: string, value: number|string) => Promise<void>; + initializeSdmData: () => void; + insertSdmData: (data: DemandCompType) => Promise<void>; + insertSdmInfoData: (data: DemandCompBizType) => Promise<void>; + getDemandCompList: (searchText: string, pageCnt: number) => Promise<void>; + actionLogin: (data: DemandCompType) => Promise<void>; + getSdmInfoStatusList: (dmdInfoId: string) => Promise<void>; + getSimilarCaseList: (part: string, value: number | string) => Promise<void>; + actionLogout: () => Promise<void>; } -export type SdmType ={ - Sdm :{ +export type SdmType = { + Sdm: { isInit: boolean; useTypesData: SelectDataType[]; demandCompList: DemandCompType[]; upHeadSimilarCaseList: TableUpperProps[]; similarCaseList: SimilarCaseType[]; - upHeadMoneyDetailList : TableUpperProps[]; + upHeadMoneyDetailList: TableUpperProps[]; moneyDetailList: MoneyDetailType[]; // 수요정보 신청현황 @@ -615,28 +616,28 @@ export type SdmType ={ defaultRules: Record<string, object> disableRules: DisableRulesType; // 로그인관련 - successNextPage : boolean; + successNextPage: boolean; successLogin: boolean; } } export const SdmState: SdmType = { - Sdm:{ - isInit:false, + Sdm: { + isInit: false, useTypesData: useTypesData, demandCompList: demandCompList, upHeadSimilarCaseList: similarCaseModalHeader, similarCaseList: similarCaseList, - upHeadMoneyDetailList : moneyDetailModalHeader, + upHeadMoneyDetailList: moneyDetailModalHeader, moneyDetailList: moneyDetailList, // 수요정보 신청현황 sdmInfoStatusList: [], - defaultRules:defaultRules, - disableRules:disableRules, + defaultRules: defaultRules, + disableRules: disableRules, upHeadSdmInfoStatus: upHeadSdmInfoStatus, // 로그인관련 - successNextPage : false, + successNextPage: false, successLogin: false, } } @@ -648,13 +649,13 @@ export const SdmReducer = (set: any): SdmActions => { insertSdmData: async (data: DemandCompType) => { const preRes = await getSdmInfoStatusList(data.dmdInfoId); - if(preRes?.dmdInfoId) { + if (preRes?.dmdInfoId) { toast.error('이미 등록된 수요처입니다.'); return; } const res = await postSdmData(data); - if(res) { + if (res) { set((state: SdmType) => ({ Sdm: { ...state.Sdm, @@ -671,7 +672,7 @@ export const SdmReducer = (set: any): SdmActions => { // 수요처 정보 getDemandCompList: async (searchText: string, pageCnt: number) => { const res = await getDemandCompList(searchText, pageCnt); - if(res.data.length === 0) { + if (res.data.length === 0) { set((state: SdmType) => ({ Sdm: { ...state.Sdm, @@ -693,7 +694,7 @@ export const SdmReducer = (set: any): SdmActions => { getSdmInfoStatusList: async (dmdInfoId: string) => { const res = await getSdmInfoStatusList(dmdInfoId); - if(res.length === 0) { + if (res.length === 0) { set((state: SdmType) => ({ Sdm: { ...state.Sdm, @@ -704,13 +705,13 @@ export const SdmReducer = (set: any): SdmActions => { } //const sdmInfoStatusList : DemandCompBizType[] = []; - const sdmInfoStatusList : DemandCompBizType[]= res.demandInfo.map((el: DemandCompBizType, index: number) => { + const sdmInfoStatusList: DemandCompBizType[] = res.demandInfo.map((el: DemandCompBizType, index: number) => { const matchingInfo = res.demandMatchings.find((matching: DemandMatchings) => matching.dmdInfoSn === el.dmdInfoSn); const status = matchingInfo?.splyFcltFlg?.trim() === 'Sub' ? '검토' : matchingInfo?.splyFcltFlg?.trim() === 'matched' ? '매칭완료' : '신청'; const ruseUtztnFcltNm = matchingInfo?.ruseUtztnFcltNm ? matchingInfo.ruseUtztnFcltNm : ''; - return( { + return ({ ...el, index: ++index, bzmnNo: res.bzmnNo, @@ -723,8 +724,8 @@ export const SdmReducer = (set: any): SdmActions => { sggCd: res.sggCd, useUsgCd: el.useUsgCd, dmndTmng: el.dmndTmng, - dmdInfoSn : el.dmdInfoSn, - status : status, + dmdInfoSn: el.dmdInfoSn, + status: status, splyCndtList: el.splyCndtList, ruseUtztnFcltNm: ruseUtztnFcltNm || '' }) @@ -749,25 +750,47 @@ export const SdmReducer = (set: any): SdmActions => { }, // 유사사례 조회 - getSimilarCaseList: async (part: string, value: number|string) => { + getSimilarCaseList: async (part: string, value: number | string) => { const res = await fetchGetSimilarCaseList(part, value); - if(res && res.length !== 0) { + const similarCaseList = res.map((el: SimilarCaseType) => { + const maskedBizNm = el.ruseBizNm + ? el.ruseBizNm.replace(/^(\s*)^\S+/, "***") // 첫 단어(공백 전까지) 치환 + : el.ruseBizNm; + + return { + ...el, + ruseBizNm: maskedBizNm, + }; + }); + console.log(similarCaseList); + + if (similarCaseList && similarCaseList.length !== 0) { set((state: SdmType) => ({ - Sdm: { + Sdm: { ...state.Sdm, - similarCaseList: res, + similarCaseList: similarCaseList, } })) return; - }else{ + } else { set((state: SdmType) => ({ - Sdm: { + Sdm: { ...state.Sdm, similarCaseList: [], } })) return; } + }, + + actionLogout: async () => { + sessionStorage.removeItem("user"); + set((state: SdmType) => ({ + Sdm: { + ...state.Sdm, + successLogin: false, + } + })) } } } diff --git a/apps/dm/app/store/wrStatus/index.tsx b/apps/dm/app/store/wrStatus/index.tsx index bd43db731..d3cc61cc4 100644 --- a/apps/dm/app/store/wrStatus/index.tsx +++ b/apps/dm/app/store/wrStatus/index.tsx @@ -85,22 +85,20 @@ export const DmStatusReducer = (set: any): DmStatusActions => { const boardData: DmStatusBoardType[] = data.data.map( (item: DmStatusDataType) => { - const regionText = cityCd.find((city) => item.region === city.val); + const regionText = item.sidoNm; //cityCd.find((city) => item.region === city.val); const sewage = item.sewage; const rainwater = item.rainwater; const reclaimed = item.reclaimed; const sewageCnt = item.sewageCnt; const rainwaterCnt = item.rainwaterCnt; const reclaimedCnt = item.reclaimedCnt; - console.log("regionText ", regionText); - if (regionText && regionText.text) { + return { - region: regionText ? regionText.text : item.region, - sewage: sewageCnt + "(" + sewage + ")", - rainwater: rainwaterCnt + "(" + rainwater + ")", - reclaimed: reclaimedCnt + "(" + reclaimed + ")", + region: regionText ? regionText : item.region, + sewage: sewageCnt + "(" + sewage.toLocaleString() + ")", + rainwater: rainwaterCnt + "(" + rainwater.toLocaleString() + ")", + reclaimed: reclaimedCnt + "(" + reclaimed.toLocaleString() + ")", }; - } } ).filter((item): item is DmStatusBoardType => item !== undefined); diff --git a/apps/dm/app/store/wrStatus/type.ts b/apps/dm/app/store/wrStatus/type.ts index 513a5fb01..2cf1b36f0 100644 --- a/apps/dm/app/store/wrStatus/type.ts +++ b/apps/dm/app/store/wrStatus/type.ts @@ -1,14 +1,16 @@ export type DmStatusDataType = { - region: string; + sidoNm: string; // 시도명 + region: string; // 시도코드 sewage: string; rainwater: string; reclaimed: string; - sewageCnt:string - rainwaterCnt:string - reclaimedCnt:string + sewageCnt:string; + rainwaterCnt:string; + reclaimedCnt:string; }; export type DmStatusBoardType = { + sidoNm: string; // 시도명 region: string; sewage: string; rainwater: string; diff --git a/apps/dm/components/ui/api/index.ts b/apps/dm/components/ui/api/index.ts index 7ba66c4ed..abb0c2c73 100644 --- a/apps/dm/components/ui/api/index.ts +++ b/apps/dm/components/ui/api/index.ts @@ -1,23 +1,33 @@ //8880 import { AUTH_USER_ID, CLAIM_NAME } from "@/src/shared/config"; -import { DEV_URL_FO, DEV_URL_WR } from "../config"; import { getCookie } from "@common/components/lib/cookie"; + const tokenId = getCookie(AUTH_USER_ID); const Authorization = getCookie(CLAIM_NAME); export const fetchType = { COMMON: "common", WR: "wr", - FO:'fo' + FO: "fo", + SN: "sn" }; +const API_VERSION = "/api/v1" +const servers = { + common: "", + local: "", + fo: "/operation-service" + API_VERSION, + wr: "/reuse-service" + API_VERSION, + sn: "/sewerage-service" + API_VERSION, +} + interface RequestParams { [key: string]: unknown; } export const getFetch = async (type: string, url: string) => { - const baseUrl = type === fetchType.FO ? DEV_URL_FO : DEV_URL_WR; + const baseUrl = servers[type]; const response = await fetch(baseUrl + url, { method: "GET", headers: { @@ -35,7 +45,8 @@ export const getFetch = async (type: string, url: string) => { }; export const postFetch = async (url: string, params: RequestParams) => { - const response = await fetch(DEV_URL_WR + url, { + const baseUrl = servers[fetchType.WR]; + const response = await fetch(baseUrl + url, { method: "POST", headers: { "Content-Type": "application/json", @@ -52,7 +63,8 @@ export const postFetch = async (url: string, params: RequestParams) => { }; export const putFetch = async (url: string, params: RequestParams) => { - const response = await fetch(DEV_URL_WR + url, { + const baseUrl = servers[fetchType.WR]; + const response = await fetch(baseUrl + url, { method: "PUT", headers: { "Content-Type": "application/json", @@ -69,7 +81,8 @@ export const putFetch = async (url: string, params: RequestParams) => { }; export const deleteFetch = async (url: string, params: RequestParams) => { - const response = await fetch(DEV_URL_WR + url, { + const baseUrl = servers[fetchType.WR]; + const response = await fetch(baseUrl + url, { method: "DELETE", headers: { "Content-Type": "application/json", diff --git a/apps/dm/components/ui/sdmInfoInsertForm/component.tsx b/apps/dm/components/ui/sdmInfoInsertForm/component.tsx index 17e4bfdc8..62b214f04 100644 --- a/apps/dm/components/ui/sdmInfoInsertForm/component.tsx +++ b/apps/dm/components/ui/sdmInfoInsertForm/component.tsx @@ -950,7 +950,7 @@ export function SdmInfoRegForm({ body={ <ModalSimilarPage sply={sply} usageCd={usageCd} money={money} /> } - bodyClass="sm:max-w-[100vw] w-[1400px] h-[900px]" + bodyClass="sm:max-w-[100vw] w-[1400px] h-[100dvh] max-h-[900px] overflow-y-auto overflow-x-hidden" open={isSimilarCase} onOpenChange={(open) => !open && setIsSimilarCase(false)} noHeaderYn="Y" diff --git a/apps/dm/middleware.ts b/apps/dm/middleware.ts index 0b2b21fbb..44d4cb390 100644 --- a/apps/dm/middleware.ts +++ b/apps/dm/middleware.ts @@ -1,13 +1,13 @@ import { NextRequest, NextResponse } from "next/server"; import { CLAIM_NAME, AUTH_USER_ID } from "./src/shared/config"; -import { SERVER_API_FO_URL, SERVER_API_WR_URL } from "./src/shared/config/env"; +// import { SERVER_API_FO_URL, SERVER_API_WR_URL } from "./src/shared/config/env"; // const CLAIM_NAME = "Authorization"; // const AUTH_USER_ID = "token-id"; const serverApiUrl = process.env.NEXT_PUBLIC_CS_SERVER_API_URL; const GEOSERVER_URL = process.env.NEXT_PUBLIC_GEOSERVER_URL || ""; const LX_GEOSERVER_URL = process.env.NEXT_PUBLIC_LX_GEOSERVER_URL || ""; -const SEWERAGE_API_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL || ""; +const GATEWAY_URL = process.env.NEXT_PUBLIC_GATEWAY_URL || ""; const BUSINESS_JUSO_API_URL = process.env.NEXT_PUBLIC_FINDADRESS_URL || ""; const AdressKey = process.env.NEXT_PRIVATE_WR_ADDRESS_KEY || ""; @@ -112,7 +112,7 @@ export function middleware(req: NextRequest) { const url = req.nextUrl.clone(); url.searchParams.set("TYPENAME", typename.replace("hasudo:", "")); queryParams = url.searchParams.toString(); - newUrl = `${SEWERAGE_API_URL}/api/v1/map/geometry${queryParams ? `?${queryParams}` : ""}`; + newUrl = `${GATEWAY_URL}/api/v1/map/geometry${queryParams ? `?${queryParams}` : ""}`; } console.log(`[Middleware] Rewriting from '/geoserver' path to: ${newUrl}`); @@ -121,24 +121,17 @@ export function middleware(req: NextRequest) { if (pathname.startsWith("/reuse-service")) { const queryParams = searchParams.toString(); - const newUrl = `${SERVER_API_WR_URL}${pathname.replace("/reuse-service", "")}${queryParams ? `?${queryParams}` : "" - }`; + const newUrl = `${GATEWAY_URL}${pathname}${queryParams ? `?${queryParams}` : ""}`; console.log(`[Middleware] Rewriting from '/reuse-service' path to: ${newUrl}`); return NextResponse.rewrite(new URL(newUrl)); - } - - if (pathname.startsWith("/operation-service")) { + } else if (pathname.startsWith("/operation-service")) { const queryParams = searchParams.toString(); - const newUrl = `${SERVER_API_FO_URL}${pathname.replace("/operation-service", "")}${queryParams ? `?${queryParams}` : "" - }`; + const newUrl = `${GATEWAY_URL}${pathname}${queryParams ? `?${queryParams}` : ""}`; console.log(`[Middleware] Rewriting from '/operation-service' path to: ${newUrl}`); return NextResponse.rewrite(new URL(newUrl)); - } - - if (pathname.startsWith("/sewerage-service")) { + } else if (pathname.startsWith("/sewerage-service")) { const queryParams = searchParams.toString(); - const newUrl = `${SEWERAGE_API_URL}${pathname.replace("/sewerage-service", "")}${queryParams ? `?${queryParams}` : "" - }`; + const newUrl = `${GATEWAY_URL}${pathname}${queryParams ? `?${queryParams}` : ""}`; console.log(`[Middleware] Rewriting from '/sewerage-service' path to: ${newUrl}`); return NextResponse.rewrite(new URL(newUrl)); } diff --git a/apps/dm/next.config.ts b/apps/dm/next.config.ts index bc21e16a4..5e1533ec9 100644 --- a/apps/dm/next.config.ts +++ b/apps/dm/next.config.ts @@ -5,6 +5,9 @@ const subDomain = const nextConfig: NextConfig = { output: "standalone", + experimental: { + proxyTimeout: 600000, + }, async rewrites() { return { beforeFiles: [ diff --git a/apps/fo/.env.local b/apps/fo/.env.local index bf6d56f58..1b80d302e 100644 --- a/apps/fo/.env.local +++ b/apps/fo/.env.local @@ -5,9 +5,10 @@ NEXT_PUBLIC_AUTH_USER_ID = token-id NEXT_PUBLIC_ACCESS_TOKEN = access-token NEXT_PUBLIC_REFRESH_TOKEN = refresh-token NEXT_PUBLIC_PROXY_HOST = http://localhost:3000 +NEXT_PUBLIC_SUB_DOMAIN = http://localhost:13000 #NEXT_PUBLIC_CM_LOGIN_PAGE_URL = http://localhost:3001/auth/login NEXT_PUBLIC_SERVER_API_URL = http://con.mind-one.co.kr:11083/operation-service -NEXT_PUBLIC_WR_SERVER_API_URL = http://con.mind-one.co.kr:11083/reuse-service/ +NEXT_PUBLIC_WR_SERVER_API_URL = http://con.mind-one.co.kr:11083/reuse-service NEXT_PUBLIC_CS_SERVER_API_URL = http://dev.onasys.co.kr:8000 NEXT_PUBLIC_GEOSERVER_URL = https://hasudo.geo.rsonesoft.com/geoserver NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver @@ -17,6 +18,7 @@ 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_CM_LOGIN_PAGE_URL = http://dev.onasys.co.kr:3000/auth/login + #NEXT_PUBLIC_SERVER_API_URL = http://localhost:8883/operation-service REACT_EDITOR=atom \ No newline at end of file diff --git a/apps/fo/.env.mindone b/apps/fo/.env.mindone index 2003db503..f014168c2 100644 --- a/apps/fo/.env.mindone +++ b/apps/fo/.env.mindone @@ -5,7 +5,7 @@ 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:11300 -NEXT_PUBLIC_SUB_DOMAIN = http://con.mind-one.co.kr:11300 +NEXT_PUBLIC_SUB_DOMAIN = http://con.mind-one.co.kr:13000 NEXT_PUBLIC_CM_LOGIN_PAGE_URL = http://dev.onasys.co.kr:3000/auth/login #NEXT_PUBLIC_SERVER_API_URL = http://con.mind-one.co.kr:11082 NEXT_PUBLIC_SERVER_API_URL = http://con.mind-one.co.kr:11083/operation-service diff --git a/apps/fo/.env.production b/apps/fo/.env.production index 45e179066..d6940b0e9 100644 --- a/apps/fo/.env.production +++ b/apps/fo/.env.production @@ -8,13 +8,12 @@ NEXT_PUBLIC_REFRESH_TOKEN = refresh-token #NEXT_PUBLIC_PROXY_HOST = https://devlarva.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/operation-service -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 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_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_SEWERAGE_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/sewerage-service NEXT_PUBLIC_DC_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/dc-service -NEXT_PUBLIC_NGII_URL = http://172.29.143.87 +NEXT_PUBLIC_SEWERAGE_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/sewerage-service +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} diff --git a/apps/fo/app/components/common/CalcRainEffect.tsx b/apps/fo/app/components/common/CalcRainEffect.tsx index beca33f78..898bea0ba 100644 --- a/apps/fo/app/components/common/CalcRainEffect.tsx +++ b/apps/fo/app/components/common/CalcRainEffect.tsx @@ -9,67 +9,111 @@ * @author jsyang */ -export default function CalcRainEffect(data : any, weather='ALL') { - /* console.log("%c Calculate RainEffect !" , "background:blue;color:white"); - console.log("%c Data Length :"+data.length+" Weather : "+weather , "background:yellow;color:blue");*/ - // 강우영향일 계산 (강우량이 3 초과이면 무조건 강우영향일) - for(let idx = 0; idx < data.length; idx++) { +export default function CalcRainEffect(data: any, weather = 'ALL') { + const rtnDataAll: any = []; + const rtnDataSunny: any = []; + const rtnDataRain: any = []; + let rtnData: any = []; + let rainEffectStd = 0; + for (let idx = 0; idx < data.length; idx++) { const item = data[idx]; - if(item){ - if(item.infoRnDay && Number(item.infoRnDay) >= 3) { // 강우량이 3 이상인 경우 - item.rainEffectDay = 1; - } else { - item.rainEffectDay = 0; - } - - if(item.infoRnDay == 'x'){ - item.infoRnDay = '0.0'; - item.toBeInfoRnDay = '0.0'; - }else{ - item.infoRnDay = Number(item.infoRnDay).toFixed(1); - item.toBeInfoRnDay = Number(item.infoRnDay).toFixed(1); - } + if (item.infoRnDay == null || item.infoRnDay == 'x' || item.infoRnDay == '') item.infoRnDay = 0; + if (Number(item.infoRnDay) >= 3) { // 강우일 + rainEffectStd = 2; + item.toBeInfoRnDay = item.infoRnDay; + } else { + rainEffectStd--; } - } - // 이틀 전, 하루 전이 강우영향일(3 이상)인 경우 해당 날도 강우영향일로 설정 - for(let idx = 0; idx < data.length; idx++) { - if(data[idx]) { - // 하루 전이 강우영향일(3 이상)인 경우 - if(idx > 0 && data[idx-1]?.infoRnDay && Number(data[idx-1]?.infoRnDay) >= 3) { - data[idx]!.rainEffectDay = 1; - } - // 이틀 전이 강우영향일(3 이상)인 경우 - if(idx > 1 && data[idx-2]?.infoRnDay && Number(data[idx-2]?.infoRnDay) >= 3) { - data[idx]!.rainEffectDay = 1; - } - } - } - - for(let idx = 0; idx < data.length; idx++) { - if(data[idx] && data[idx]?.rainEffectDay === 1) { - if(idx > 1){ - // 전날 비가 왔고 오늘 강우량 3미만인 경우 전날 강우량이 3이상이면 오늘 toBeInfoRnDay을 전날 강우량으로 설정 - if(data[idx-1]?.infoRnDay && Number(data[idx]?.infoRnDay) < 3 && Number(data[idx-1]?.infoRnDay.toString().trim()) >= 3) { - data[idx]!.toBeInfoRnDay = data[idx-1]!.infoRnDay; - } - } - if(idx > 2){ + if (rainEffectStd < 0) { // 청천일 + item.rainEffectDay = 0; + rtnDataSunny.push(item); + } else if (rainEffectStd >= 0) { // 강우일 + 강우영향일 + item.rainEffectDay = 1; + if (idx > 2) { // 전전날 비가 왔고 오늘 강우량 3미만인 경우 전전날 강우량이 3이상이면 오늘 toBeInfoRnDay을 전전날 강우량으로 설정 - if(data[idx-2]?.infoRnDay && Number(data[idx]?.infoRnDay) < 3 && Number(data[idx-2]?.infoRnDay.toString().trim()) >= 3) { - data[idx]!.toBeInfoRnDay = data[idx-2]!.infoRnDay; + if (data[idx - 2]?.infoRnDay && Number(data[idx]?.infoRnDay) < 3 && Number(data[idx - 2]?.infoRnDay.toString().trim()) >= 3) { + data[idx]!.toBeInfoRnDay = data[idx - 2]!.infoRnDay; } } + if (idx > 1) { + // 전날 비가 왔고 오늘 강우량 3미만인 경우 전날 강우량이 3이상이면 오늘 toBeInfoRnDay을 전날 강우량으로 설정 + if (data[idx - 1]?.infoRnDay && Number(data[idx]?.infoRnDay) < 3 && Number(data[idx - 1]?.infoRnDay.toString().trim()) >= 3) { + data[idx]!.toBeInfoRnDay = data[idx - 1]!.infoRnDay; + } + } + rtnDataRain.push(item); } + + rtnDataAll.push(item); } - let rtnData : any = []; + + // // 강우영향일 계산 (강우량이 3 초과이면 무조건 강우영향일) + // for(let idx = 0; idx < data.length; idx++) { + // const item = data[idx]; + // if(item){ + // if(item.infoRnDay && Number(item.infoRnDay) >= 3) { // 강우량이 3 이상인 경우 + // item.rainEffectDay = 1; + // } else { + // item.rainEffectDay = 0; + // } + + // if(item.infoRnDay == 'x'){ + // item.infoRnDay = '0.0'; + // item.toBeInfoRnDay = '0.0'; + // }else{ + // item.infoRnDay = Number(item.infoRnDay).toFixed(1); + // item.toBeInfoRnDay = Number(item.infoRnDay).toFixed(1); + // } + // } + // } + + // // 이틀 전, 하루 전이 강우영향일(3 이상)인 경우 해당 날도 강우영향일로 설정 + // for(let idx = 0; idx < data.length; idx++) { + // if(data[idx]) { + // // 하루 전이 강우영향일(3 이상)인 경우 + // if(idx > 0 && data[idx-1]?.infoRnDay && Number(data[idx-1]?.infoRnDay) >= 3) { + // data[idx]!.rainEffectDay = 1; + // } + // // 이틀 전이 강우영향일(3 이상)인 경우 + // if(idx > 1 && data[idx-2]?.infoRnDay && Number(data[idx-2]?.infoRnDay) >= 3) { + // data[idx]!.rainEffectDay = 1; + // } + // } + // } + + // for(let idx = 0; idx < data.length; idx++) { + // if(data[idx] && data[idx]?.rainEffectDay === 1) { + // if(idx > 1){ + // // 전날 비가 왔고 오늘 강우량 3미만인 경우 전날 강우량이 3이상이면 오늘 toBeInfoRnDay을 전날 강우량으로 설정 + // if(data[idx-1]?.infoRnDay && Number(data[idx]?.infoRnDay) < 3 && Number(data[idx-1]?.infoRnDay.toString().trim()) >= 3) { + // data[idx]!.toBeInfoRnDay = data[idx-1]!.infoRnDay; + // } + // } + // if(idx > 2){ + // // 전전날 비가 왔고 오늘 강우량 3미만인 경우 전전날 강우량이 3이상이면 오늘 toBeInfoRnDay을 전전날 강우량으로 설정 + // if(data[idx-2]?.infoRnDay && Number(data[idx]?.infoRnDay) < 3 && Number(data[idx-2]?.infoRnDay.toString().trim()) >= 3) { + // data[idx]!.toBeInfoRnDay = data[idx-2]!.infoRnDay; + // } + // } + // } + // } + + // if (weather === 'ALL') { + // rtnData = data; + // } else if (weather === 'SUNNY') { + // rtnData = data.filter((item: any) => item.rainEffectDay === 0); + // } else { + // rtnData = data.filter((item: any) => item.rainEffectDay === 1); + // } + if (weather === 'ALL') { - rtnData = data; + rtnData = rtnDataAll; } else if (weather === 'SUNNY') { - rtnData = data.filter((item: any) => item.rainEffectDay === 0); + rtnData = rtnDataSunny; } else { - rtnData = data.filter((item: any) => item.rainEffectDay === 1); + rtnData = rtnDataRain; } return rtnData; } \ No newline at end of file diff --git a/apps/fo/app/components/common/DataForm.tsx b/apps/fo/app/components/common/DataForm.tsx index 48b428e32..2c99129b2 100644 --- a/apps/fo/app/components/common/DataForm.tsx +++ b/apps/fo/app/components/common/DataForm.tsx @@ -4,12 +4,39 @@ item: Record<string, unknown>; } -export const roundNumber = (value : any, decimals: number) => { +/** + * yyyymm 형태의 문자열을 받아 해당 월의 일수를 반환 + * @param {string} yyyymm - 예: "202502", "202412" + * @returns {number} 해당 월의 일수 (예: 28, 29, 30, 31) + */ +export const getDaysInMonth = (yyyymm: string) => { + if (typeof yyyymm !== 'string' || yyyymm.length !== 6) { + throw new Error("입력은 'yyyymm' 형식의 문자열이어야 합니다. 예: '202502'"); + } + + const year = parseInt(yyyymm.slice(0, 4), 10); // "2025" -> 2025 + const month = parseInt(yyyymm.slice(4, 6), 10); // "02" -> 2 + + if (isNaN(year) || isNaN(month) || month < 1 || month > 12) { + throw new Error("유효한 년/월이 아닙니다."); + } + + // Date(year, month, 0) -> 전달의 마지막 날 + // month에 '다음달'을 넣고, 일(day)에 0을 주면 해당 월의 마지막 날짜가 됨 + return new Date(year, month, 0).getDate(); +} + +export const roundNumber = (value: any, decimals: number) => { if (typeof value === 'number') { return addCommas((Math.round(value * Math.pow(10, decimals)) / Math.pow(10, decimals)).toFixed(decimals)); - } else if (typeof value === 'string' && !isNaN(Number(value))) { - return addCommas(Number(value).toFixed(decimals)); + } else if (typeof value === 'string') { + // 콤마가 포함된 문자열 처리 ('1,083.0' 같은 경우) + const cleanedValue = value.replace(/,/g, ''); + if (!isNaN(Number(cleanedValue))) { + return addCommas(Number(cleanedValue).toFixed(decimals)); + } } + return value; // 변환 불가능한 경우 원래 값 반환 } export const addCommas = (value: string | number): any => { @@ -18,48 +45,48 @@ export const addCommas = (value: string | number): any => { index === 0 ? part : part.replace(/(\d{3})/g, '$1,') ).join('.'); } else if (typeof value === 'string') { - return value.includes('.')?value.split('.').map((part, index) => + return value.includes('.') ? value.split('.').map((part, index) => index === 0 ? part.replace(/\B(?=(\d{3})+(?!\d))/g, ",") : part - ).join('.'):String(value).replace(/\B(?=(\d{3})+(?!\d))/g, ","); + ).join('.') : String(value).replace(/\B(?=(\d{3})+(?!\d))/g, ","); } } export default function DataForm({ type, column, item }: DataFormProps) { - if(item) { + if (item) { const value = item[column]; - switch(type){ + switch (type) { case 'date': - if(value && typeof value === 'string' && value.length >= 8) + if (value && typeof value === 'string' && value.length >= 8 && !value.includes('-')) item[column] = value.substring(0, 4) + "-" + value.substring(4, 6) + "-" + value.substring(6, 8); - break; + break; case 'number': - if(value && value != '') { + if (value && value != '') { item[column] = value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ","); } - break; + break; case 'float0': - if(value) { - item[column] = item[column] = String(value).includes('.')?String(Math.round(parseFloat(value.toString().replace(/,/g, '')) * 1) / 1).split('.').map((part, index) => - index === 0 ? part.replace(/\B(?=(\d{3})+(?!\d))/g, ",") : part - ).join('.') - : String(value).replace(/\B(?=(\d{3})+(?!\d))/g, ","); + if (value) { + item[column] = item[column] = String(value).includes('.') ? String(Math.round(parseFloat(value.toString().replace(/,/g, '')) * 1) / 1).split('.').map((part, index) => + index === 0 ? part.replace(/\B(?=(\d{3})+(?!\d))/g, ",") : part + ).join('.') + : String(value).replace(/\B(?=(\d{3})+(?!\d))/g, ","); } - break; + break; case 'float1': - if(value) { + if (value) { item[column] = roundNumber(value, 1); } - break; + break; case 'float2': - if(value) { + if (value) { item[column] = roundNumber(value, 2); } - break; + break; case 'float3': - if(value) { + if (value) { item[column] = roundNumber(value, 3); } - break; + break; } } } \ No newline at end of file diff --git a/apps/fo/app/components/common/FacilityTermModalHeader.tsx b/apps/fo/app/components/common/FacilityTermModalHeader.tsx index 52cee2ea3..a46b3652d 100644 --- a/apps/fo/app/components/common/FacilityTermModalHeader.tsx +++ b/apps/fo/app/components/common/FacilityTermModalHeader.tsx @@ -4,14 +4,14 @@ import ListDiv from '@common/business_components/ui/list_div'; import { Table, TableCell, TableHead, TableRow } from '@common/components/ui'; import { useCallback, useEffect, useRef, useState } from 'react'; -type FacilityTermModalHeaderProps<T extends { msrDt: string }> = { - fcltyCd : string; +type FacilityTermModalHeaderProps<T extends { msrDt: string }> = { // T는 반드시 msrDt: string 필드를 가져야 한다 + fcltyCd: string; fcltyName: string; tabName: string; searchYear: string; searchList: T[]; ref: React.RefObject<ReturnType<T>>; - queryClick:(returnType:ReturnType<T>) => void; + queryClick: (returnType: ReturnType<T>) => void; } export type ReturnType<T> = { @@ -21,27 +21,28 @@ export type ReturnType<T> = { filteredList: T[]; } -export default function FacilityTermModalHeader<T extends { msrDt: string }>({ +export default function FacilityTermModalHeader<T extends { msrDt: string }>({ searchList, - fcltyName, - tabName, + fcltyName, + tabName, searchYear, queryClick, ref }: FacilityTermModalHeaderProps<T & { msrDt: string }>) { - const {Common} = useFoStore((state) => state); + const { Common } = useFoStore((state) => state); const { selectFlowPanel } = Common; const [startDate, setStartDate] = useState<Date>(new Date(Number(searchYear), 0, 1)); const [endDate, setEndDate] = useState<Date>(new Date(Number(searchYear), 11, 31)); const startPickerRef = useRef<HTMLDivElement>(null); const endPickerRef = useRef<HTMLDivElement>(null); - const [flowPanel, setFlowPanel] = useState<string>(selectFlowPanel[0]?.val ?? '00'); + const [flowPanel, setFlowPanel] = useState<string>(selectFlowPanel[0]?.val ?? '0'); // 강수구분 : 초기값 "전체" const [copySearchList, setCopySearchList] = useState<T[]>(searchList); const [standSearchList, setStandSearchList] = useState<T[]>(searchList); const isDateInRanges = (date: Date) => { const standYear = parseInt(searchYear); - const dateRanges:{start:Date, end:Date}[] = []; + const isLeapYear = (standYear % 4 === 0 && standYear % 100 !== 0) || (standYear % 400 === 0); + const dateRanges: { start: Date, end: Date }[] = []; if (tabName.includes("_summer")) { dateRanges.push({ @@ -51,7 +52,7 @@ export default function FacilityTermModalHeader<T extends { msrDt: string }>({ } else if (tabName.includes("_winter")) { dateRanges.push({ start: new Date(standYear, 0, 1), - end: new Date(standYear, 1, 28) + end: new Date(standYear, 1, isLeapYear ? 29 : 28) }); } else { dateRanges.push({ @@ -70,67 +71,69 @@ export default function FacilityTermModalHeader<T extends { msrDt: string }>({ useEffect(() => { let startDate = new Date(Number(searchYear), 0, 1); let endDate = new Date(Number(searchYear), 11, 31); + const standYear = Number(searchYear); + const isLeapYear = (standYear % 4 === 0 && standYear % 100 !== 0) || (standYear % 400 === 0); - if(tabName.includes("_summer")) { - startDate = new Date(Number(searchYear), 5, 1); - endDate = new Date(Number(searchYear), 7, 31); + if (tabName.includes("_summer")) { // 하절기 + startDate = new Date(Number(searchYear), 5, 1); // 6월 ~ + endDate = new Date(Number(searchYear), 7, 31); // 8월 setStartDate(startDate); setEndDate(endDate); - } else if (tabName.includes("_winter")) { - startDate = new Date(Number(searchYear), 0, 1); - endDate = new Date(Number(searchYear), 1, 28); + } else if (tabName.includes("_winter")) { // 동절기 + startDate = new Date(Number(searchYear), 0, 1); // 1월 ~ + endDate = new Date(Number(searchYear), 1, isLeapYear ? 29 : 28); // 2월 setStartDate(startDate); setEndDate(endDate); - } else { + } else { // 연간 startDate = new Date(Number(searchYear), 0, 1); endDate = new Date(Number(searchYear), 11, 31); setStartDate(startDate); setEndDate(endDate); } - + let filteredData: T[] = []; - if(tabName.includes("_rain")) { + if (tabName.includes("_rain")) { // 강우시 filteredData = searchList.filter((item) => { const itemDate = new Date((item as T & { msrDt: string }).msrDt); const itemDateOnly = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate()); const startDateOnly = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate()); const endDateOnly = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate()); const rainEffectDay = (item as T & { rainEffectDay: number }).rainEffectDay; - - if(flowPanel === '2'){ + + if (flowPanel === '2') { //3~10mm미만 const infoRnDay = (item as T & { toBeInfoRnDay: number }).toBeInfoRnDay; return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 3 && infoRnDay < 10; - } else if(flowPanel === '3'){ + } else if (flowPanel === '3') { //10~20mm미만 const infoRnDay = (item as T & { toBeInfoRnDay: number }).toBeInfoRnDay; return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 10 && infoRnDay < 20; - } else if(flowPanel === '4'){ + } else if (flowPanel === '4') { //20~30mm미만 const infoRnDay = (item as T & { toBeInfoRnDay: number }).toBeInfoRnDay; return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 20 && infoRnDay < 30; - } else if(flowPanel === '5'){ + } else if (flowPanel === '5') { //30~40mm미만 const infoRnDay = (item as T & { toBeInfoRnDay: number }).toBeInfoRnDay; return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 30 && infoRnDay < 40; - } else if(flowPanel === '6'){ + } else if (flowPanel === '6') { //40~50mm미만 const infoRnDay = (item as T & { toBeInfoRnDay: number }).toBeInfoRnDay; return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 40 && infoRnDay < 50; - } else if(flowPanel === '7'){ + } else if (flowPanel === '7') { //50~60mm미만 const infoRnDay = (item as T & { toBeInfoRnDay: number }).toBeInfoRnDay; return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 50 && infoRnDay < 60; - } else if(flowPanel === '8'){ + } else if (flowPanel === '8') { const infoRnDay = (item as T & { toBeInfoRnDay: number }).toBeInfoRnDay; return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 60; } else { - return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && (rainEffectDay ===1); + return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && (rainEffectDay === 1); } }); - } else if(tabName.includes("_chuncheon")) { + } else if (tabName.includes("_chuncheon")) { // 청천시 filteredData = searchList.filter((item) => { const itemDate = new Date((item as T & { msrDt: string }).msrDt); const itemDateOnly = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate()); const startDateOnly = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate()); const endDateOnly = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate()); const rainEffectDay = (item as T & { rainEffectDay: number }).rainEffectDay; - return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && rainEffectDay==0; + return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && rainEffectDay == 0; }); } else { filteredData = searchList.filter((item) => { @@ -146,7 +149,7 @@ export default function FacilityTermModalHeader<T extends { msrDt: string }>({ }, [searchList, searchYear, tabName, flowPanel]); const handleStartDateSelect = (day: Date | undefined) => { - if(day && day > endDate) { + if (day && day > endDate) { alert('종료일은 시작일 이전일 수 없습니다.'); return; } @@ -169,7 +172,7 @@ export default function FacilityTermModalHeader<T extends { msrDt: string }>({ }; const handleEndDateSelect = (day: Date | undefined) => { - if(day && day < startDate) { + if (day && day < startDate) { alert('종료일은 시작일 이후일 수 없습니다.'); return; } @@ -190,7 +193,7 @@ export default function FacilityTermModalHeader<T extends { msrDt: string }>({ }); setCopySearchList(filteredData); }; - + const fncSetFilteredList = useCallback(() => { queryClick({ startDate: startDate, @@ -203,9 +206,9 @@ export default function FacilityTermModalHeader<T extends { msrDt: string }>({ useEffect(() => { fncSetFilteredList(); }, [fncSetFilteredList]); - + useEffect(() => { - if(ref && ref.current) { + if (ref && ref.current) { const customRef = ref.current; customRef.startDate = startDate; customRef.endDate = endDate; @@ -222,18 +225,18 @@ export default function FacilityTermModalHeader<T extends { msrDt: string }>({ <TableRow className="!h-[20px]"> <TableHead className="w-[120px]">{'시설명'}</TableHead> {tabName === "tab_term_rain" ? <TableCell className="w-[300px]">{fcltyName}</TableCell> : - <TableCell className="w-[380px]">{fcltyName}</TableCell>} + <TableCell className="w-[380px]">{fcltyName}</TableCell>} {tabName === "tab_term_rain" && ( <> <TableHead className="w-[120px]">{'강수구분'}</TableHead> <TableCell className="w-[250px]"> <div className="justify-center"> - <SelectBox - selectArray={selectFlowPanel} + <SelectBox + selectArray={selectFlowPanel} value={flowPanel} onSelectValue={(val) => setFlowPanel(val.toString())} - className="mx-3 w-[150px]" - label="" + className="mx-3 w-[150px]" + label="" selectClass="w-full p-1 border rounded bg-white" /> </div> @@ -244,7 +247,7 @@ export default function FacilityTermModalHeader<T extends { msrDt: string }>({ <TableCell className=""> <div className="flex"> <div ref={startPickerRef} className="flex justify-center"> - <DatePickerRange + <DatePickerRange mode="single" selected={startDate} onSelect={(date) => handleStartDateSelect(date as Date)} @@ -258,7 +261,7 @@ export default function FacilityTermModalHeader<T extends { msrDt: string }>({ <DatePickerRange mode="single" selected={endDate} - onSelect={(date) => handleEndDateSelect(date as Date)} + onSelect={(date) => handleEndDateSelect(date as Date)} className="w-full p-1 border rounded bg-white" disabled={(date) => !isDateInRanges(date)} defaultMonth={endDate} diff --git a/apps/fo/app/pages/(with-layout)/dashboard/facilityInfo/page.tsx b/apps/fo/app/pages/(with-layout)/dashboard/facilityInfo/page.tsx index dd689f2fa..e65fe3cc8 100644 --- a/apps/fo/app/pages/(with-layout)/dashboard/facilityInfo/page.tsx +++ b/apps/fo/app/pages/(with-layout)/dashboard/facilityInfo/page.tsx @@ -271,7 +271,7 @@ export default function ProcessFacilitySearch({searchYear = '', fcltyList = []}: (head.id === 'flowRate' && index === 0)?<TableCell rowSpan={5} className="text-center" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>: (head.id === 'flowRate' && index === 5)?<TableCell rowSpan={6} className="text-center" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>: (head.id === 'flowRate' && index === 11)?<TableCell rowSpan={2} className="text-center" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>: - (head.id === 'groundWaterAvg'||head.id === 'groundWaterSummer'||head.id === 'groundWaterWinter')&&(index === 0)?<TableCell rowSpan={7} className="text-right" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>: + (head.id === 'groundWaterAvg'||head.id === 'groundWaterSummer'||head.id === 'groundWaterWinter')&&(index === 0)?<TableCell rowSpan={7} className="text-right" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{Number.isNaN(el[head.id]) ? "" : el[head.id]}</TableCell>: (head.id === 'groundWaterAvg'||head.id === 'groundWaterSummer'||head.id === 'groundWaterWinter')&&(index === 7)?<TableCell rowSpan={6} className="text-right" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>: (head.id === 'rdi'&&index === 0)?<TableCell rowSpan={7} className="text-right" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>: (head.id === 'rdi'&&index === 7)?<TableCell rowSpan={6} className="text-right" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>: diff --git a/apps/fo/app/pages/(with-layout)/dashboard/page.tsx b/apps/fo/app/pages/(with-layout)/dashboard/page.tsx index 8d431cf75..24d71e719 100644 --- a/apps/fo/app/pages/(with-layout)/dashboard/page.tsx +++ b/apps/fo/app/pages/(with-layout)/dashboard/page.tsx @@ -6,7 +6,10 @@ "use client" import { useFoStore } from "@/app/store"; -import { ContentsDiv, downloadExcel, downloadMultipleExcel, filePickedBlobArray, MainDiv, SelectBox, UITab, type SelectDataType } from "@common/business_components"; +import { + ContentsDiv, downloadExcelFormat, downloadMultipleExcel, filePickedBlobArrayFormat, MainDiv + , SelectBox, UITab, type SelectDataType +} from "@common/business_components"; import { Button, TabsContent } from "@common/components/ui"; import { useEffect, useState } from "react"; import DashBoardFclty from "../dashboardMain/page"; @@ -40,34 +43,30 @@ export const generateYearDates = (searchYear: string) => { return dates; } +type FacilityType = { + bscFctCd: string; + bscFctNm: string; + fcltyCpc: number | ''; + trtFrfNm: string | ''; + efflWqltStndrdNm: string | ''; +} + export default function DashBoard() { // 입력값 상태 관리 - const { Common, initializeCommonData, searchMainGraph, searchRainManage, searchOperationStatus, OperationStatus, RainManage, MainGraph, PlanFclty } = useFoStore((state) => state); + const { Common, initializeCommonData, searchMainGraph, searchRainManage, searchOperationStatus + , OperationStatus, RainManage, MainGraph, PlanFclty } = useFoStore((state) => state); const [activeTab, setActiveTab] = useState<string>('facilityInfo'); - const [filteredSigunData, setFilteredSigunData] = useState<SelectDataType[]>([{ text: "전체", val: '0' }]); const [sidoValue, setSidoValue] = useState<string>('0'); const [sigunValue, setSigunValue] = useState<string>('0'); - const { - isInit, - selectSearchYear, - btnClass, - selectSidoData, - selectSigunData, - } = Common; - - const { - planFcltyList - } = PlanFclty; - - const { - operationalStatusList - } = OperationStatus; - - const { - excelList - } = RainManage; - + const { isInit, selectSearchYear, btnClass, selectSidoData, selectSigunData, } = Common; + const defaultSearchYear = + selectSearchYear.find((item) => item.selected)?.val || + selectSearchYear[0]?.val || + "0"; + const { planFcltyList } = PlanFclty; + const { operationalStatusList } = OperationStatus; + const { excelList } = RainManage; const { chart_inflowRain, chart_bodRain, @@ -79,10 +78,10 @@ export default function DashBoard() { } = MainGraph; /******************선택된 데이터******************/ - // 연도 - const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || '0'); + + const [searchYear, setSearchYear] = useState<string>(defaultSearchYear); // 연도 // 시설명 - const [fcltyList, setFcltyList] = useState<{ bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string | '', efflWqltStndrdNm: string | '' }>({ + const [fcltyList, setFcltyList] = useState<FacilityType>({ bscFctCd: '11000SW001R', bscFctNm: '난지', fcltyCpc: 860000, @@ -90,7 +89,7 @@ export default function DashBoard() { efflWqltStndrdNm: '500톤 이상(Ⅲ지역)' }); - const [searchFcltyList, setSearchFcltyList] = useState<{ bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string | '', efflWqltStndrdNm: string | '' }>({ + const [searchFcltyList, setSearchFcltyList] = useState<FacilityType>({ bscFctCd: '', bscFctNm: '', fcltyCpc: '', @@ -111,7 +110,7 @@ export default function DashBoard() { } }, [isInit, initializeCommonData]); - const handleFacilityChange = (facility: { bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string | '', efflWqltStndrdNm: string | '' }) => { + const handleFacilityChange = (facility: FacilityType) => { setFcltyList(facility); }; @@ -143,13 +142,11 @@ export default function DashBoard() { } } - const excelDownload = () => { + const excelDownload = async () => { if (!fcltyList.bscFctCd) return; switch (activeTab) { case 'facilityInfo': // 시설정보 및 운영현황 - searchOperationStatus(false, searchYear, fcltyList.bscFctCd).then(() => { setTimeout(() => { makeExcelData(); }, 200); }); - const makeExcelData = async () => { fetch(`/mainDashBoard.xlsx`, {}).then(async (response) => { if (!response.ok) { @@ -199,20 +196,27 @@ export default function DashBoard() { headerNum: 3, headerRange: 'A1:G2', sheetName: '운영실태', - startCol: 'A' + startCol: 'A', + decimalRange0: 'B3:G2,B9:B11', + decimalRange1: 'B5:G5', }, { data: excelData2, headerNum: 4, headerRange: 'A1:Q3', sheetName: '운영실태', - startCol: 'C' + startCol: 'C', + decimalRange0: 'C4:O8', + decimalRange1: 'C9:L10,M11:O11', + decimalRange3: 'C11:L16', } ]; // 버퍼에 있는 엑셀데이터 전달 후 json으로 변환 - await filePickedBlobArray(blobRet, '시설정보및운영현황', sheetParameter); + await filePickedBlobArrayFormat(blobRet, '시설정보및운영현황', sheetParameter); }); }; + await searchOperationStatus(false, searchYear, fcltyList.bscFctCd); + await makeExcelData(); break; case 'mainGraph': // 주요그래프 @@ -255,60 +259,6 @@ export default function DashBoard() { headerRange: 'A1:J1', sheetName: '주요그래프' } - /* { - data: [['날짜', '강수량'],...chart_inflowRain.map((item) => [item.label, roundNumber(item.barVal0, 1)])], - headerNum: 1, - headerRange: 'A1:B1', - sheetName: '강수량' - }, - { - data: [['날짜', '유입하수량'],...chart_inflowRain.map((item) => [item.label, roundNumber(item.lineVal1, 0)])], - headerNum: 1, - headerRange: 'A1:B1', - sheetName: '유입하수량' - }, - { - data: [['날짜', '시설용량'],...chart_inflowRain.map((item) => [item.label, roundNumber(item.lineVal0, 0)])], - headerNum: 1, - headerRange: 'A1:B1', - sheetName: '시설용량' - }, - { - data: [['날짜', '유입BOD'],...chart_bodRain.map((item) => [item.label, roundNumber(item.lineVal0, 1)])], - headerNum: 1, - headerRange: 'A1:B1', - sheetName: '유입BOD' - }, - { - data: [['날짜', '유입T-P'],...chart_inflTp.map((item) => [item.label, roundNumber(item.val, 3)])], - headerNum: 1, - headerRange: 'A1:B1', - sheetName: '유입T-P' - }, - { - data: [['날짜', '총인유입T-P'],...chart_chongInTp.map((item) => [item.label, roundNumber(item.val, 3)])], - headerNum: 1, - headerRange: 'A1:B1', - sheetName: '총인유입T-P' - }, - { - data: [['날짜', '방류T-P'],...chart_efflTp.map((item) => [item.label, roundNumber(item.val, 3)])], - headerNum: 1, - headerRange: 'A1:B1', - sheetName: '방류T-P' - }, - { - data: [['날짜', '유입T-N'],...chart_inflTn.map((item) => [item.label, roundNumber(item.val, 3)])], - headerNum: 1, - headerRange: 'A1:B1', - sheetName: '유입T-N' - }, - { - data: [['날짜', '방류T-N'],...chart_efflTn.map((item) => [item.label, roundNumber(item.val, 3)])], - headerNum: 1, - headerRange: 'A1:B1', - sheetName: '방류T-N' - } */ ]; downloadMultipleExcel('주요그래프', sheetParameter); @@ -317,7 +267,15 @@ export default function DashBoard() { case 'rainManage': // 강우일 운영현황 searchRainManage(searchYear, fcltyList.bscFctCd, true); if (excelList && excelList.length > 0) { - downloadExcel({ data: excelList as unknown as JSON[], fileName: '강우일 운영현황', sheetNameArray: ['강우일 운영현황'], headerRange: 'A1:T1' }); + downloadExcelFormat({ + data: excelList as unknown as JSON[], + fileName: '강우일 운영현황', + sheetNameArray: ['강우일 운영현황'], + headerRange: 'A1:T1', + decimalArray0: [2,3], + decimalArray1: [1,4,5,6,7,10,11,12,15,16,17], + decimalArray3: [8,9,13,14,18,19], + }); } break; // Add other cases for different tabs as needed @@ -342,7 +300,7 @@ export default function DashBoard() { trtFrfNm: '표준활성슬러지법, MLE, A2O', efflWqltStndrdNm: '500톤 이상(Ⅲ지역)' }); - setSearchYear(selectSearchYear[0]?.val || '0'); + setSearchYear(defaultSearchYear); searchFcltyList.bscFctNm = '난지'; setSidoValue('0'); diff --git a/apps/fo/app/pages/(with-layout)/dashboard/rainManage/page.tsx b/apps/fo/app/pages/(with-layout)/dashboard/rainManage/page.tsx index e084bf04d..55e6275c8 100644 --- a/apps/fo/app/pages/(with-layout)/dashboard/rainManage/page.tsx +++ b/apps/fo/app/pages/(with-layout)/dashboard/rainManage/page.tsx @@ -15,27 +15,13 @@ export type facilityInfoProps = { fcltyList?: { bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string, efflWqltStndrdNm: string }[]; }; -export default function RainManageTab({searchYear = '', fcltyList = []}: facilityInfoProps) { - - const { - Common, - searchRainManage, - RainManage - } = useFoStore((state) => state); +export default function RainManageTab({ searchYear = '', fcltyList = [] }: facilityInfoProps) { + const { Common, searchRainManage, RainManage } = useFoStore((state) => state); const [hasSearched, setHasSearched] = useState(false); - // 이전 시설코드 저장 - const [prevFacilityCode, setPrevFacilityCode] = useState<string>(''); - - const { - isInit, - } = Common; - - const { - upHeadListRainManage, - rainManageList, - } = RainManage; - + const [prevFacilityCode, setPrevFacilityCode] = useState<string>(''); // 이전 시설코드 저장 + const { isInit, } = Common; + const { upHeadListRainManage, rainManageList, } = RainManage; const tableData = fcltyList; useEffect(() => { @@ -50,17 +36,17 @@ export default function RainManageTab({searchYear = '', fcltyList = []}: facilit useEffect(() => { setHasSearched(false); }, [searchYear]); - + return ( <div> - <UiTable + <UiTable headName="" tableData={rainManageList} headlist={upHeadListRainManage} pageSize={0} total={0} cellClick={undefined} - onPageChange={() => {}} + onPageChange={() => { }} widthFixed={true} > </UiTable> diff --git a/apps/fo/app/pages/(with-layout)/facilityPrecipitation/modal/page.tsx b/apps/fo/app/pages/(with-layout)/facilityPrecipitation/modal/page.tsx index 1bd8a6c89..811f1c0ff 100644 --- a/apps/fo/app/pages/(with-layout)/facilityPrecipitation/modal/page.tsx +++ b/apps/fo/app/pages/(with-layout)/facilityPrecipitation/modal/page.tsx @@ -9,7 +9,7 @@ import CalcRainEffect from "@/app/components/common/CalcRainEffect"; import { roundNumber } from "@/app/components/common/DataForm"; import { useFoStore } from "@/app/store"; import type { PrecipitationPropsDetailProps } from "@/app/store/facilityPrecipitation"; -import { DatePickerRange, downloadExcel, UiTable } from "@common/business_components/ui"; +import { DatePickerRange, downloadExcelHeaders, UiTable } from "@common/business_components/ui"; import ListDiv from "@common/business_components/ui/list_div"; import { Button, Table, TableCell, TableHead, TableRow } from "@common/components/ui"; import { useEffect, useRef, useState } from "react"; @@ -45,7 +45,7 @@ export default function PrecipitationModal(props: PrecipitationModalProps) { const standYear = parseInt(props.searchYear); return [{ start: new Date(standYear, 0, 1), - end: new Date(standYear, 11, 31) + end: new Date(standYear, 11, 31) //월의 index는 0부터 시작 }]; }; @@ -112,7 +112,6 @@ export default function PrecipitationModal(props: PrecipitationModalProps) { const getTableData = () => { const totals = calculateTotals(); totals.infoRnDay = roundNumber(totals.infoRnDay, 1); - return [ { bscFctCd: "", @@ -157,19 +156,26 @@ export default function PrecipitationModal(props: PrecipitationModalProps) { const excelDownload = () => { const totData = getTableData(); - const excelDownList: any[] = []; + + // 헤더 순서를 명시적으로 정의 + const headers = ["날짜", "강수량(mm/일)", "강우일(일)", "강우영향일(일)"] as const; + + const excelDownList: Record<string, string | number>[] = []; if(totData.length > 0){ totData.forEach((item) => { - excelDownList.push({ - "날짜": item.rainDt, - "강수량(mm/일)": roundNumber(item.infoRnDay, 1), - "강우일(일)": roundNumber(item.rainDay, 0), - "강우영향일(일)": roundNumber(item.rainEffectDay, 0), - }); + // 순서를 보장하기 위해 헤더 순서대로 객체 생성 + const row: Record<string, string | number> = {}; + row[headers[0]] = item.rainDt; + row[headers[1]] = roundNumber(item.infoRnDay, 1); + row[headers[2]] = roundNumber(item.rainDay, 0); + row[headers[3]] = roundNumber(item.rainEffectDay, 0); + excelDownList.push(row); }); } + if (excelDownList && excelDownList.length > 0) { - downloadExcel({ data: JSON.parse(JSON.stringify(excelDownList)), fileName: props.searchYear+'년 '+props.fcltyName + ' 강수현황', sheetNameArray: ['지하수유입량'], headerRange: 'A1:D1' }); + downloadExcelHeaders({ data: JSON.parse(JSON.stringify(excelDownList)), fileName: props.searchYear+'년 '+props.fcltyName + ' 강수현황' + , sheetNameArray: ['지하수유입량'], headerRange: 'A1:D1', headers }); } else if (totData.length == 0) { alert('다운받을 데이터가 없습니다.'); } diff --git a/apps/fo/app/pages/(with-layout)/facilityPrecipitation/page.tsx b/apps/fo/app/pages/(with-layout)/facilityPrecipitation/page.tsx index 0e46505e0..9570320d4 100644 --- a/apps/fo/app/pages/(with-layout)/facilityPrecipitation/page.tsx +++ b/apps/fo/app/pages/(with-layout)/facilityPrecipitation/page.tsx @@ -5,11 +5,11 @@ */ "use client" -import { useFoStore } from "@/app/store"; +import { StoreContext, useFoStore } from "@/app/store"; import type { TableUpperProps } from "@/app/store/common"; -import { ContentsDiv, downloadExcel, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components"; +import { ContentsDiv, downloadExcelFormat, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components"; import ListDiv from "@common/business_components/ui/list_div"; -import { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useContext, useEffect, useRef, useState } from "react"; import PrecipitationModal from "./modal/page"; import LoadingOverlay from "@common/business_components/ui/loadingBar"; import UnderComment from "@/app/components/common/UnderComment"; @@ -22,6 +22,11 @@ export default function FacilityPrecipitation() { ,initializeCommonData ,searchFacilityPrecipitationList } = useFoStore((state) => state) || { processAreaList: { processAreaList: [] }}; + const store = useContext(StoreContext); + + if (!store) { + throw new Error("StoreContext is not available. Ensure StoreProvider is mounted."); + } const { isInit, @@ -37,6 +42,11 @@ export default function FacilityPrecipitation() { btnClass } = Common; + const defaultSearchYear = + selectSearchYear.find((item) => item.selected)?.val || + selectSearchYear[0]?.val || + "0"; + const { labelArray, } = FacilityPrecipitationSearch; @@ -45,7 +55,6 @@ export default function FacilityPrecipitation() { upHeadList1, upHeadList2, upHeadList3, - excelList, facilityPrecipitation, facilityPrecipitationCount, isLoading, @@ -90,8 +99,9 @@ export default function FacilityPrecipitation() { // 시설상태 const [operationValue, setOperationValue] = useState<string>("0"); // 조회기간 - const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0"); - + const [searchYear, setSearchYear] = useState<string>(defaultSearchYear); + // 엑셀다운로드 트리거 + const [isExcelDownClick, setIsExcelDownClick] = useState<boolean>(false); /** * 페이지 함수 */ @@ -102,7 +112,7 @@ export default function FacilityPrecipitation() { // 상태값 설정 setFcltCpct(selectUpdownData[0]?.val || "0"); setOperationValue(selectOperationData[0]?.val || "0"); - setSearchYear(selectSearchYear[0]?.val || "0"); + setSearchYear(defaultSearchYear); // SearchInputSelect 초기화 setFcltyName(""); if (searchInputRef.current) { @@ -111,7 +121,7 @@ export default function FacilityPrecipitation() { input.value = ""; } } - searchFacilityPrecipitationList(1, 10, selectSearchYear[0]?.val || "0"); + searchFacilityPrecipitationList(1, 10, defaultSearchYear); } // 모달 닫기 @@ -134,20 +144,9 @@ export default function FacilityPrecipitation() { useEffect(() => { if (!isInit) { initializeCommonData(); - searchFacilityPrecipitationList(1, 10, selectSearchYear[0]?.val || "0"); + searchFacilityPrecipitationList(1, 10, defaultSearchYear); } - }, [isInit, initializeCommonData, searchFacilityPrecipitationList, selectSearchYear]); - - useEffect(() => { - if (excelList && excelList.length > 0) { - downloadExcel({ - data: excelList as unknown as JSON[], - fileName: '시설별 강수현황', - sheetNameArray: ['시설별 강수현황'], - headerRange: 'A1:N1' - }); - } - }, [excelList]); + }, [isInit, initializeCommonData, searchFacilityPrecipitationList, defaultSearchYear]); useEffect(() => { if (facilityPrecipitation.length > 0) { @@ -159,7 +158,7 @@ export default function FacilityPrecipitation() { * 콜백함수 */ // 조회 - const searchFacilityPrecipitation = useCallback(({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=20) => { + const searchFacilityPrecipitation = useCallback(async ({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=20) => { let nameCd, partStr switch (partValue) { case 'R': @@ -177,9 +176,9 @@ export default function FacilityPrecipitation() { break; } if (partStr.length > 1 || operationValue!== '0' || fcltCpct !== '0' ||nameCd ||(fcltyName.trim() !== "" && fcltyName.trim() !== null)) { - searchFacilityPrecipitationList(pageNo, pageCnt, searchYear, partStr, nameCd || '0', fcltCpct, operationValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined); + await searchFacilityPrecipitationList(pageNo, pageCnt, searchYear, partStr, nameCd || '0', fcltCpct, operationValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined); } else { - searchFacilityPrecipitationList(pageNo, pageCnt, searchYear); + await searchFacilityPrecipitationList(pageNo, pageCnt, searchYear); } }, [operationValue, fcltyName, searchFacilityPrecipitationList, searchYear, fcltCpct]); @@ -190,9 +189,43 @@ export default function FacilityPrecipitation() { }, [upHeadList1, upHeadList2, upHeadList3]); // 엑셀다운로드 - const excelDownload = useCallback((searchReturn: ReturnType) => { - searchFacilityPrecipitation(searchReturn, 1, 10000); - }, [searchFacilityPrecipitation]); + const excelDownload = useCallback(async (searchReturn: ReturnType) => { + if (isExcelDownClick) return; + // if (ref.current.partValue === 'A') { // 행정별 + // if (!ref.current.sidoValue || ref.current.sidoValue === '0') { + // alert('엑셀 다운로드는 시도 조건을 선택한 후 이용 가능합니다.'); + // return; + // } + // } else if (ref.current.partValue === 'R') { // 유역별 + // if (!ref.current.regionValue || ref.current.regionValue === '0') { + // alert('엑셀 다운로드는 유역 조건을 선택한 후 이용 가능합니다.'); + // return; + // } + // } else if (ref.current.partValue === 'G') { // 환경청별 + // if (!ref.current.envPartValue || ref.current.envPartValue === '0') { + // alert('엑셀 다운로드는 환경청 조건을 선택한 후 이용 가능합니다.'); + // return; + // } + // } + setIsExcelDownClick(true); + try { + await searchFacilityPrecipitation(searchReturn, 1, 10000); + const latestExcelList = store.getState().FacilityPrecipitation.excelList || []; + if (latestExcelList.length > 0) { + downloadExcelFormat({ + data: latestExcelList as unknown as JSON[], + fileName: '시설별 강수현황', + sheetNameArray: ['시설별 강수현황'], + headerRange: 'A1:N1', + decimalArray0: [9, 11, 12, 13], + }); + } else { + alert("조회된 결과가 없습니다."); + } + } finally { + setIsExcelDownClick(false); + } + }, [isExcelDownClick, searchFacilityPrecipitation, store]); // 페이지 변경 const handlePageChange = useCallback((page: number) => { @@ -209,10 +242,13 @@ export default function FacilityPrecipitation() { <ContentsDiv> <SearchBaseForm leftChildren={<> - <SelectBox className="col-span-3" label={labelArray[3] as string} selectArray={selectUpdownData} onSelectValue={(val) => setFcltCpct(val.toString())} value={fcltCpct} selectClass={undefined} /> + <SelectBox className="col-span-3" label={labelArray[3] as string} selectArray={selectUpdownData} + onSelectValue={(val) => setFcltCpct(val.toString())} value={fcltCpct} selectClass={undefined} /> <SearchInput className={"col-span-3"} label={labelArray[4] as string} textValue={fcltyName} setText={setFcltyName} /> - <SelectBox className="col-span-3" label={labelArray[6] as string} selectArray={selectSearchYear} onSelectValue={(val) => setSearchYear(val.toString())} value={searchYear} selectClass={undefined} /> - <SelectBox className="col-span-3" label={labelArray[5] as string} selectArray={selectOperationData} onSelectValue={(val) => setOperationValue(val.toString())} value={operationValue} selectClass={undefined} /> + <SelectBox className="col-span-3" label={labelArray[6] as string} selectArray={selectSearchYear} + onSelectValue={(val) => setSearchYear(val.toString())} value={searchYear} selectClass={undefined} /> + <SelectBox className="col-span-3" label={labelArray[5] as string} selectArray={selectOperationData} + onSelectValue={(val) => setOperationValue(val.toString())} value={operationValue} selectClass={undefined} /> </>} rightChildren={undefined} selectSidoData={selectSidoData} @@ -224,18 +260,22 @@ export default function FacilityPrecipitation() { btnClass={btnClass} queryClick={chkVal} excelClick={excelDownload} + excelDisabled={isExcelDownClick} initClick={resetVal} ref={ref} isAdmin={false}/> - <ListDiv> - <LoadingOverlay isLoading={isLoading} /> - <div className="h-full w-full overflow-x-scroll min-[1200px]:overflow-x-hidden"> - <div className="w-[120%] min-[1200px]:w-[100%]"> - <UiTable cellClick={(index: number | undefined) => index !== undefined && cellClick(index)} headName={"시설별 강수현황"} tableData={mainList} headlist={upheadList} pageSize={10} total={total} resetPageNo={resetPage} onPageChange={handlePageChange} widthFixed={true} /> + <ListDiv> + <LoadingOverlay isLoading={isLoading} /> + <div className="h-full w-full overflow-x-scroll min-[1200px]:overflow-x-hidden"> + <div className="w-[120%] min-[1200px]:w-[100%]"> + <UiTable cellClick={(index: number | undefined) => index !== undefined && cellClick(index)} + headName={"시설별 강수현황"} + tableData={mainList} headlist={upheadList} pageSize={10} total={total} + resetPageNo={resetPage} onPageChange={handlePageChange} widthFixed={true} /> + </div> + <UnderComment divHeight={"3"} commentArray={["강우일은 3mm 이상만 해당 / 강우영향일은 강우일 및 강우일 후 연속 2일간으로 정의"]} /> </div> - <UnderComment divHeight={"3"} commentArray={["강우일은 3mm 이상만 해당 / 강우영향일은 강우일 및 강우일 후 연속 2일간으로 정의"]} /> - </div> - </ListDiv> - </ContentsDiv> + </ListDiv> + </ContentsDiv> {selectedRow !== null && ( <UiModal body={<PrecipitationModal searchYear={searchYear} fcltyName={fcltyNm} facilityCd={fcltyCd} detailList={gridListPrecipitationDetail}></PrecipitationModal>} @@ -248,4 +288,4 @@ export default function FacilityPrecipitation() { )} </MainDiv> ); -} \ No newline at end of file +} diff --git a/apps/fo/app/pages/(with-layout)/facilityWaterQlty/modal/page.tsx b/apps/fo/app/pages/(with-layout)/facilityWaterQlty/modal/page.tsx index c10fd50cf..9c6d5360a 100644 --- a/apps/fo/app/pages/(with-layout)/facilityWaterQlty/modal/page.tsx +++ b/apps/fo/app/pages/(with-layout)/facilityWaterQlty/modal/page.tsx @@ -25,10 +25,10 @@ const commentArrayRain = [ "강우량 3mm 이상만 날과 강우영향일(강우일+2일)의 날" ]; -type ModalProps ={ +type ModalProps = { fcltyName: string; - facilityCd : string; - searchYear : string; + facilityCd: string; + searchYear: string; } const tabList1 = [ @@ -50,15 +50,13 @@ const tabList3 = [ { value: 'tab_term_winter_chongin', label: '동절기' } ]; -export default function WaterQualityModal({fcltyName='', facilityCd='', searchYear=new Date().getFullYear().toString()} : ModalProps) { +export default function WaterQualityModal({ fcltyName = '', facilityCd = '', searchYear = new Date().getFullYear().toString() }: ModalProps) { const [tabName, setTabName] = useState<string>('tab_num1'); - - // 그리드 관련 - const {FacilityWaterQltyModal, - searchFacilityWaterQltyListHist, - Common - } = useFoStore((state) => state); - const {btnClass, COD_YEAR} = Common; + // 날짜 필터링 관련 상태 + const [copyYearList, setCopyYearList] = useState<FacilityWaterQltyProps[]>([]); + const [chongInWaterQltyYearList, setChongInWaterQltyYearList] = useState<FacilityWaterQltyProps[]>([]); + const { FacilityWaterQltyModal, searchFacilityWaterQltyListHist, Common } = useFoStore((state) => state); + const { btnClass, COD_YEAR } = Common; const { upHeadListModalYear, upHeadListModalYearCod, @@ -71,7 +69,17 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe const prevExcelListRef = useRef<modalExcelType[] | null>(null); const prevChoinginExcelListRef = useRef<modalChoinginExcelType[] | null>(null); - + + useEffect(() => { + searchFacilityWaterQltyListHist(searchYear, facilityCd); + }, [searchYear, facilityCd, searchFacilityWaterQltyListHist]); + + useEffect(() => { + const filteredYearData = setFilterAfterData(facilityWaterQltyYearList); + setCopyYearList(filteredYearData as FacilityWaterQltyProps[]); + setChongInWaterQltyYearList(facilityWaterQltyYearList); + }, [facilityWaterQltyYearList]); + // 필터링 된 후 최대, 최소, 평균 값 계산 const setFilterAfterData = (filteredData: FacilityWaterQltyProps[]) => { // 쉼표가 포함된 문자열을 숫자로 변환하는 함수 @@ -100,7 +108,7 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe let maxNoProcessInflAmnt = 0; let maxInfoRnDay = 0; - + // 초기값 설정 let minEfflWqltBod = filteredData.length > 0 ? parseNumber(filteredData[0]?.efflWqltBod) : 0; let minEfflWqltTn = filteredData.length > 0 ? parseNumber(filteredData[0]?.efflWqltTn) : 0; @@ -108,14 +116,14 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe let minefflWqltToc = filteredData.length > 0 ? parseNumber(filteredData[0]?.efflWqltToc) : 0; let minefflWqltCod = filteredData.length > 0 ? parseNumber(filteredData[0]?.efflWqltCod) : 0; let minEfflWqltSs = filteredData.length > 0 ? parseNumber(filteredData[0]?.efflWqltSs) : 0; - + let mininflAssctBfrWqltBod = filteredData.length > 0 ? parseNumber(filteredData[0]?.inflAssctBfrWqltBod) : 0; let mininflAssctBfrWqltTn = filteredData.length > 0 ? parseNumber(filteredData[0]?.inflAssctBfrWqltTn) : 0; let minInflAssctBfrWqltTp = filteredData.length > 0 ? parseNumber(filteredData[0]?.inflAssctBfrWqltTp) : 0; let mininflAssctBfrWqltToc = filteredData.length > 0 ? parseNumber(filteredData[0]?.inflAssctBfrWqltToc) : 0; let mininflAssctBfrWqltCod = filteredData.length > 0 ? parseNumber(filteredData[0]?.inflAssctBfrWqltCod) : 0; let mininflAssctBfrWqltSs = filteredData.length > 0 ? parseNumber(filteredData[0]?.inflAssctBfrWqltSs) : 0; - + let minTpInflWqltBod = 99999999; let minTpInflWqltTp = 99999999; let minNoProcessInflAmnt = 99999999; @@ -143,27 +151,27 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe let noProcessInflAmntCnt = 0; let avgInfoRnDay = 0; - + filteredData.forEach((item: FacilityWaterQltyProps) => { const efflWqltBod = parseNumber(item.efflWqltBod); const efflWqltTn = parseNumber(item.efflWqltTn); const efflWqltTp = parseNumber(item.efflWqltTp); const efflWqltToc = parseNumber(item.efflWqltToc); - const efflWqltCod = parseNumber(item.efflWqltCod?item.efflWqltCod.replaceAll("(","").replaceAll(")",""):0); + const efflWqltCod = parseNumber(item.efflWqltCod ? item.efflWqltCod.replaceAll("(", "").replaceAll(")", "") : 0); const efflWqltSs = parseNumber(item.efflWqltSs); const inflAssctBfrWqltBod = parseNumber(item.inflAssctBfrWqltBod); const inflAssctBfrWqltTn = parseNumber(item.inflAssctBfrWqltTn); const inflAssctBfrWqltTp = parseNumber(item.inflAssctBfrWqltTp); const inflAssctBfrWqltToc = parseNumber(item.inflAssctBfrWqltToc); - const inflAssctBfrWqltCod = parseNumber(item.inflAssctBfrWqltCod?item.inflAssctBfrWqltCod.replaceAll("(","").replaceAll(")",""):0); + const inflAssctBfrWqltCod = parseNumber(item.inflAssctBfrWqltCod ? item.inflAssctBfrWqltCod.replaceAll("(", "").replaceAll(")", "") : 0); const inflAssctBfrWqltSs = parseNumber(item.inflAssctBfrWqltSs); const tpInflWqltBod = parseNumber(item.tpInflWqltBod); const tpInflWqltTp = parseNumber(item.tpInflWqltTp); - if(item.tpInflWqltBod != null && item.tpInflWqltBod.trim() != '' && item.tpInflWqltBod != undefined ){ + if (item.tpInflWqltBod != null && item.tpInflWqltBod.trim() != '' && item.tpInflWqltBod != undefined) { tpInflWqltBodCnt += 1; } - if(item.tpInflWqltTp != null && item.tpInflWqltTp.trim() != '' && item.tpInflWqltTp != undefined ){ + if (item.tpInflWqltTp != null && item.tpInflWqltTp.trim() != '' && item.tpInflWqltTp != undefined) { tpInflWqltTpCnt += 1; } @@ -175,7 +183,7 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe maxefflWqltToc = Math.max(maxefflWqltToc, efflWqltToc); maxefflWqltCod = Math.max(maxefflWqltCod, efflWqltCod); maxEfflWqltSs = Math.max(maxEfflWqltSs, efflWqltSs); - + maxinflAssctBfrWqltBod = Math.max(maxinflAssctBfrWqltBod, inflAssctBfrWqltBod); maxinflAssctBfrWqltTn = Math.max(maxinflAssctBfrWqltTn, inflAssctBfrWqltTn); maxInflAssctBfrWqltTp = Math.max(maxInflAssctBfrWqltTp, inflAssctBfrWqltTp); @@ -202,11 +210,11 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe mininflAssctBfrWqltCod = Math.min(mininflAssctBfrWqltToc, inflAssctBfrWqltCod); mininflAssctBfrWqltSs = Math.min(mininflAssctBfrWqltSs, inflAssctBfrWqltSs); - if(item.tpInflWqltBod != null && item.tpInflWqltBod.trim() != '' && item.tpInflWqltBod != undefined ){ + if (item.tpInflWqltBod != null && item.tpInflWqltBod.trim() != '' && item.tpInflWqltBod != undefined) { minTpInflWqltBod = Math.min(minTpInflWqltBod, tpInflWqltBod); avgTpInflWqltBod += tpInflWqltBod; } - if(item.tpInflWqltTp != null && item.tpInflWqltTp.trim() != '' && item.tpInflWqltTp != undefined ){ + if (item.tpInflWqltTp != null && item.tpInflWqltTp.trim() != '' && item.tpInflWqltTp != undefined) { minTpInflWqltTp = Math.min(minTpInflWqltTp, tpInflWqltTp); avgTpInflWqltTp += tpInflWqltTp; } @@ -227,12 +235,12 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe avginflAssctBfrWqltCod += inflAssctBfrWqltCod; avginflAssctBfrWqltSs += inflAssctBfrWqltSs; - if(item.noProcessInflAmnt && item.noProcessInflAmnt.toString().trim() != '' && item.noProcessInflAmnt != undefined){ - const noProcessInflAmnt = Number(item.noProcessInflAmnt.toString().replaceAll(',','')); + if (item.noProcessInflAmnt && item.noProcessInflAmnt.toString().trim() != '' && item.noProcessInflAmnt != undefined) { + const noProcessInflAmnt = Number(item.noProcessInflAmnt.toString().replaceAll(',', '')); avgNoProcessInflAmnt += noProcessInflAmnt; maxNoProcessInflAmnt = Math.max(maxNoProcessInflAmnt, noProcessInflAmnt); minNoProcessInflAmnt = Math.min(minNoProcessInflAmnt, noProcessInflAmnt); - noProcessInflAmntCnt +=1; + noProcessInflAmntCnt += 1; } avgInfoRnDay += infoRnDay; @@ -241,18 +249,18 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe const newFilteredData = [ { msrDt: '평균', - efflWqltBod: filteredData.length==0 || avgEfflWqltBod===0?'0.0':roundNumber((avgEfflWqltBod / filteredData.length), 1), - efflWqltToc: filteredData.length==0 || avgefflWqltToc===0?'0.0':roundNumber((avgefflWqltToc / filteredData.length), 1), - efflWqltCod: filteredData.length==0 || avgefflWqltCod===0?"(0.0)":"("+roundNumber((avgefflWqltCod / filteredData.length), 1)+")", - efflWqltSs: filteredData.length==0 || avgEfflWqltSs===0?'0.0':roundNumber((avgEfflWqltSs / filteredData.length), 1), - efflWqltTn: filteredData.length==0 || avgEfflWqltTn ===0?'0.000':roundNumber((avgEfflWqltTn / filteredData.length), 3), - efflWqltTp: filteredData.length==0 || avgEfflWqltTp ===0?'0.000':roundNumber((avgEfflWqltTp / filteredData.length), 3), - inflAssctBfrWqltBod: filteredData.length==0 || avginflAssctBfrWqltBod===0?'0.0':roundNumber((avginflAssctBfrWqltBod / filteredData.length), 1), - inflAssctBfrWqltTn: filteredData.length==0 || avginflAssctBfrWqltTn===0?'0.000':roundNumber((avginflAssctBfrWqltTn / filteredData.length), 3), - inflAssctBfrWqltTp: filteredData.length==0 || avgInflAssctBfrWqltTp===0?'0.000':roundNumber((avgInflAssctBfrWqltTp / filteredData.length), 3), - inflAssctBfrWqltToc: filteredData.length==0 || avginflAssctBfrWqltToc===0?'0.0':roundNumber((avginflAssctBfrWqltToc / filteredData.length), 1), - inflAssctBfrWqltCod: filteredData.length==0 || avginflAssctBfrWqltCod===0?'(0.0)':"("+roundNumber((avginflAssctBfrWqltCod / filteredData.length), 1)+")", - inflAssctBfrWqltSs: filteredData.length==0 || avginflAssctBfrWqltSs===0?'0.0':roundNumber((avginflAssctBfrWqltSs / filteredData.length), 1), + efflWqltBod: filteredData.length == 0 || avgEfflWqltBod === 0 ? '0.0' : roundNumber((avgEfflWqltBod / filteredData.length), 1), + efflWqltToc: filteredData.length == 0 || avgefflWqltToc === 0 ? '0.0' : roundNumber((avgefflWqltToc / filteredData.length), 1), + efflWqltCod: filteredData.length == 0 || avgefflWqltCod === 0 ? "(0.0)" : "(" + roundNumber((avgefflWqltCod / filteredData.length), 1) + ")", + efflWqltSs: filteredData.length == 0 || avgEfflWqltSs === 0 ? '0.0' : roundNumber((avgEfflWqltSs / filteredData.length), 1), + efflWqltTn: filteredData.length == 0 || avgEfflWqltTn === 0 ? '0.000' : roundNumber((avgEfflWqltTn / filteredData.length), 3), + efflWqltTp: filteredData.length == 0 || avgEfflWqltTp === 0 ? '0.000' : roundNumber((avgEfflWqltTp / filteredData.length), 3), + inflAssctBfrWqltBod: filteredData.length == 0 || avginflAssctBfrWqltBod === 0 ? '0.0' : roundNumber((avginflAssctBfrWqltBod / filteredData.length), 1), + inflAssctBfrWqltTn: filteredData.length == 0 || avginflAssctBfrWqltTn === 0 ? '0.000' : roundNumber((avginflAssctBfrWqltTn / filteredData.length), 3), + inflAssctBfrWqltTp: filteredData.length == 0 || avgInflAssctBfrWqltTp === 0 ? '0.000' : roundNumber((avgInflAssctBfrWqltTp / filteredData.length), 3), + inflAssctBfrWqltToc: filteredData.length == 0 || avginflAssctBfrWqltToc === 0 ? '0.0' : roundNumber((avginflAssctBfrWqltToc / filteredData.length), 1), + inflAssctBfrWqltCod: filteredData.length == 0 || avginflAssctBfrWqltCod === 0 ? '(0.0)' : "(" + roundNumber((avginflAssctBfrWqltCod / filteredData.length), 1) + ")", + inflAssctBfrWqltSs: filteredData.length == 0 || avginflAssctBfrWqltSs === 0 ? '0.0' : roundNumber((avginflAssctBfrWqltSs / filteredData.length), 1), bscFctCd: "", bscFctNm: "", lclgvrnmntNm: "", @@ -260,25 +268,25 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe sggNm: "", addr: "", fctCpc: 0, - noProcessInflAmnt : roundNumber(avgNoProcessInflAmnt / noProcessInflAmntCnt, 0), - infoRnDay: filteredData.length==0 || avgInfoRnDay===0?'0.0':roundNumber(avgInfoRnDay / filteredData.length, 1), - tpInflWqltBod: filteredData.length==0 || avgTpInflWqltBod ===0 ?'0.0':roundNumber(avgTpInflWqltBod / tpInflWqltBodCnt, 1), - tpInflWqltTp: filteredData.length==0 || avgTpInflWqltTp === 0?'0.000':roundNumber(avgTpInflWqltTp / tpInflWqltTpCnt, 3) + noProcessInflAmnt: roundNumber(avgNoProcessInflAmnt / noProcessInflAmntCnt, 0), + infoRnDay: filteredData.length == 0 || avgInfoRnDay === 0 ? '0.0' : roundNumber(avgInfoRnDay / filteredData.length, 1), + tpInflWqltBod: filteredData.length == 0 || avgTpInflWqltBod === 0 ? '0.0' : roundNumber(avgTpInflWqltBod / tpInflWqltBodCnt, 1), + tpInflWqltTp: filteredData.length == 0 || avgTpInflWqltTp === 0 ? '0.000' : roundNumber(avgTpInflWqltTp / tpInflWqltTpCnt, 3) }, { - msrDt : '최대', - efflWqltBod : Math.round(maxEfflWqltBod * 10) / 10?roundNumber(maxEfflWqltBod, 1):'0.0', - efflWqltToc : Math.round(maxefflWqltToc * 10) / 10?roundNumber(maxefflWqltToc, 1):'0.0', - efflWqltCod : Math.round(maxefflWqltCod * 10) / 10?"("+roundNumber(maxefflWqltCod, 1)+")":'(0.0)', - efflWqltSs : Math.round(maxEfflWqltSs * 10) / 10?roundNumber(maxEfflWqltSs, 1):'0.0', - efflWqltTn : Math.round(maxEfflWqltTn * 1000) / 1000?roundNumber(maxEfflWqltTn, 3):'0.000', - efflWqltTp : Math.round(maxEfflWqltTp * 1000) / 1000?roundNumber(maxEfflWqltTp, 3):'0.000', - inflAssctBfrWqltBod : Math.round(maxinflAssctBfrWqltBod * 10) / 10?roundNumber(maxinflAssctBfrWqltBod, 1):'0.0', - inflAssctBfrWqltTn : Math.round(maxinflAssctBfrWqltTn * 1000) / 1000?roundNumber(maxinflAssctBfrWqltTn, 3):'0.000', - inflAssctBfrWqltTp : Math.round(maxInflAssctBfrWqltTp * 1000) / 1000?roundNumber(maxInflAssctBfrWqltTp, 3):'0.000', - inflAssctBfrWqltToc : Math.round(maxinflAssctBfrWqltToc * 10) / 10?roundNumber(maxinflAssctBfrWqltToc, 1):'0.0', - inflAssctBfrWqltCod : Math.round(maxinflAssctBfrWqltCod * 10) / 10?"("+roundNumber(maxinflAssctBfrWqltCod, 1)+")":'(0.0)', - inflAssctBfrWqltSs : Math.round(maxinflAssctBfrWqltSs * 10) / 10?roundNumber(maxinflAssctBfrWqltSs, 1):'0.0', + msrDt: '최대', + efflWqltBod: Math.round(maxEfflWqltBod * 10) / 10 ? roundNumber(maxEfflWqltBod, 1) : '0.0', + efflWqltToc: Math.round(maxefflWqltToc * 10) / 10 ? roundNumber(maxefflWqltToc, 1) : '0.0', + efflWqltCod: Math.round(maxefflWqltCod * 10) / 10 ? "(" + roundNumber(maxefflWqltCod, 1) + ")" : '(0.0)', + efflWqltSs: Math.round(maxEfflWqltSs * 10) / 10 ? roundNumber(maxEfflWqltSs, 1) : '0.0', + efflWqltTn: Math.round(maxEfflWqltTn * 1000) / 1000 ? roundNumber(maxEfflWqltTn, 3) : '0.000', + efflWqltTp: Math.round(maxEfflWqltTp * 1000) / 1000 ? roundNumber(maxEfflWqltTp, 3) : '0.000', + inflAssctBfrWqltBod: Math.round(maxinflAssctBfrWqltBod * 10) / 10 ? roundNumber(maxinflAssctBfrWqltBod, 1) : '0.0', + inflAssctBfrWqltTn: Math.round(maxinflAssctBfrWqltTn * 1000) / 1000 ? roundNumber(maxinflAssctBfrWqltTn, 3) : '0.000', + inflAssctBfrWqltTp: Math.round(maxInflAssctBfrWqltTp * 1000) / 1000 ? roundNumber(maxInflAssctBfrWqltTp, 3) : '0.000', + inflAssctBfrWqltToc: Math.round(maxinflAssctBfrWqltToc * 10) / 10 ? roundNumber(maxinflAssctBfrWqltToc, 1) : '0.0', + inflAssctBfrWqltCod: Math.round(maxinflAssctBfrWqltCod * 10) / 10 ? "(" + roundNumber(maxinflAssctBfrWqltCod, 1) + ")" : '(0.0)', + inflAssctBfrWqltSs: Math.round(maxinflAssctBfrWqltSs * 10) / 10 ? roundNumber(maxinflAssctBfrWqltSs, 1) : '0.0', bscFctCd: "", bscFctNm: "", lclgvrnmntNm: "", @@ -286,25 +294,25 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe sggNm: "", addr: "", fctCpc: 0, - noProcessInflAmnt : roundNumber(maxNoProcessInflAmnt, 0), - infoRnDay: Math.round(maxInfoRnDay * 10) / 10?roundNumber(maxInfoRnDay, 1):'0.0', - tpInflWqltBod: Math.round(maxTpInflWqltBod * 10) / 10?roundNumber(maxTpInflWqltBod, 1):'0.0', - tpInflWqltTp: Math.round(maxTpInflWqltTp * 1000) / 1000?roundNumber(maxTpInflWqltTp, 3):'0.000' + noProcessInflAmnt: roundNumber(maxNoProcessInflAmnt, 0), + infoRnDay: Math.round(maxInfoRnDay * 10) / 10 ? roundNumber(maxInfoRnDay, 1) : '0.0', + tpInflWqltBod: Math.round(maxTpInflWqltBod * 10) / 10 ? roundNumber(maxTpInflWqltBod, 1) : '0.0', + tpInflWqltTp: Math.round(maxTpInflWqltTp * 1000) / 1000 ? roundNumber(maxTpInflWqltTp, 3) : '0.000' }, { - msrDt : '최소', - efflWqltBod : Math.round(minEfflWqltBod * 10) / 10?roundNumber(minEfflWqltBod, 1) : '0.0', - efflWqltToc : Math.round(minefflWqltToc * 10) / 10?roundNumber(minefflWqltToc, 1) : '0.0', - efflWqltCod : Math.round(minefflWqltCod * 10) / 10?"("+roundNumber(minefflWqltCod, 1)+")" : '(0.0)', - efflWqltSs : Math.round(minEfflWqltSs * 10) / 10?roundNumber(minEfflWqltSs, 1) : '0.0', - efflWqltTn : Math.round(minEfflWqltTn * 1000) / 1000?roundNumber(minEfflWqltTn, 3) : '0.000', - efflWqltTp : Math.round(minEfflWqltTp * 1000) / 1000?roundNumber(minEfflWqltTp, 3) : '0.000', - inflAssctBfrWqltBod : Math.round(mininflAssctBfrWqltBod * 10) / 10?roundNumber(mininflAssctBfrWqltBod, 1) : '0.0', - inflAssctBfrWqltTn : Math.round(mininflAssctBfrWqltTn * 1000) / 1000?roundNumber(mininflAssctBfrWqltTn, 3) : '0.000', - inflAssctBfrWqltTp : Math.round(minInflAssctBfrWqltTp * 1000) / 1000?roundNumber(minInflAssctBfrWqltTp, 3) : '0.000', - inflAssctBfrWqltToc : Math.round(mininflAssctBfrWqltToc * 10) / 10?roundNumber(mininflAssctBfrWqltToc, 1) : '0.0', - inflAssctBfrWqltCod : Math.round(mininflAssctBfrWqltCod * 10) / 10?"("+roundNumber(mininflAssctBfrWqltCod, 1)+")" : '(0.0)', - inflAssctBfrWqltSs : Math.round(mininflAssctBfrWqltSs * 10) / 10?roundNumber(mininflAssctBfrWqltSs, 1) : '0.0', + msrDt: '최소', + efflWqltBod: Math.round(minEfflWqltBod * 10) / 10 ? roundNumber(minEfflWqltBod, 1) : '0.0', + efflWqltToc: Math.round(minefflWqltToc * 10) / 10 ? roundNumber(minefflWqltToc, 1) : '0.0', + efflWqltCod: Math.round(minefflWqltCod * 10) / 10 ? "(" + roundNumber(minefflWqltCod, 1) + ")" : '(0.0)', + efflWqltSs: Math.round(minEfflWqltSs * 10) / 10 ? roundNumber(minEfflWqltSs, 1) : '0.0', + efflWqltTn: Math.round(minEfflWqltTn * 1000) / 1000 ? roundNumber(minEfflWqltTn, 3) : '0.000', + efflWqltTp: Math.round(minEfflWqltTp * 1000) / 1000 ? roundNumber(minEfflWqltTp, 3) : '0.000', + inflAssctBfrWqltBod: Math.round(mininflAssctBfrWqltBod * 10) / 10 ? roundNumber(mininflAssctBfrWqltBod, 1) : '0.0', + inflAssctBfrWqltTn: Math.round(mininflAssctBfrWqltTn * 1000) / 1000 ? roundNumber(mininflAssctBfrWqltTn, 3) : '0.000', + inflAssctBfrWqltTp: Math.round(minInflAssctBfrWqltTp * 1000) / 1000 ? roundNumber(minInflAssctBfrWqltTp, 3) : '0.000', + inflAssctBfrWqltToc: Math.round(mininflAssctBfrWqltToc * 10) / 10 ? roundNumber(mininflAssctBfrWqltToc, 1) : '0.0', + inflAssctBfrWqltCod: Math.round(mininflAssctBfrWqltCod * 10) / 10 ? "(" + roundNumber(mininflAssctBfrWqltCod, 1) + ")" : '(0.0)', + inflAssctBfrWqltSs: Math.round(mininflAssctBfrWqltSs * 10) / 10 ? roundNumber(mininflAssctBfrWqltSs, 1) : '0.0', bscFctCd: "", bscFctNm: "", lclgvrnmntNm: "", @@ -312,10 +320,10 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe sggNm: "", addr: "", fctCpc: 0, - noProcessInflAmnt : roundNumber(minNoProcessInflAmnt, 0), - infoRnDay: Math.round(minInfoRnDay * 10) / 10?'0.0':roundNumber(minInfoRnDay, 1), - tpInflWqltBod: Math.round(minTpInflWqltBod * 10) / 10 && minTpInflWqltBod != 99999999?roundNumber(minTpInflWqltBod, 1):'0.0', - tpInflWqltTp: Math.round(minTpInflWqltTp * 1000) / 1000 && minTpInflWqltTp != 99999999?roundNumber(minTpInflWqltTp, 3):'0.000' + noProcessInflAmnt: roundNumber(minNoProcessInflAmnt, 0), + infoRnDay: Math.round(minInfoRnDay * 10) / 10 ? roundNumber(minInfoRnDay, 1) : '0.0', + tpInflWqltBod: Math.round(minTpInflWqltBod * 10) / 10 && minTpInflWqltBod != 99999999 ? roundNumber(minTpInflWqltBod, 1) : '0.0', + tpInflWqltTp: Math.round(minTpInflWqltTp * 1000) / 1000 && minTpInflWqltTp != 99999999 ? roundNumber(minTpInflWqltTp, 3) : '0.000' }, ...filteredData ]; @@ -323,20 +331,6 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe return newFilteredData; } - // 날짜 필터링 관련 상태 - const [copyYearList, setCopyYearList] = useState<FacilityWaterQltyProps[]>([]); - const [chongInWaterQltyYearList, setChongInWaterQltyYearList] = useState<FacilityWaterQltyProps[]>([]); - - useEffect(() => { - searchFacilityWaterQltyListHist(searchYear, facilityCd); - }, [searchYear, facilityCd, searchFacilityWaterQltyListHist]); - - useEffect(() => { - const filteredYearData = setFilterAfterData(facilityWaterQltyYearList); - setCopyYearList(filteredYearData as FacilityWaterQltyProps[]); - setChongInWaterQltyYearList(filteredYearData as FacilityWaterQltyProps[]); - }, [facilityWaterQltyYearList]); - const yesterDay = new Date(); yesterDay.setDate(yesterDay.getDate() - 1); const yesterDayStr = yesterDay.toISOString().split('T')[0]; @@ -350,14 +344,14 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe const filterData = useCallback((returnType: ReturnType<FacilityWaterQltyProps>) => { const filteredData = setFilterAfterData(returnType.filteredList); - setCopyYearList(filteredData as FacilityWaterQltyProps[]); + setCopyYearList(filteredData as FacilityWaterQltyProps[]); // 처리시설/총인설비 수질현황 }, []); const excelDownload = () => { - - if(tabName === 'tab_num1'){ + + if (tabName === 'tab_num1') { //처리시설 수질현황 const excelDownList: modalExcelType[] = []; - if(copyYearList.length > 0){ + if (copyYearList.length > 0) { copyYearList.forEach((item) => { excelDownList.push({ 날짜: item.msrDt, @@ -371,44 +365,48 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe "유출SS(mg/L)": CommaToNumber(item.efflWqltSs), "유출TN(mg/L)": CommaToNumber(item.efflWqltTn), "유출TP(mg/L)": CommaToNumber(item.efflWqltTp), - "강우량(mm/일)": CommaToNumber(item.infoRnDay?item.infoRnDay.toString():"0.0"), + "강우량(mm/일)": CommaToNumber(item.infoRnDay ? item.infoRnDay.toString() : "0.0"), }); }); } if (excelDownList && excelDownList.length > 0) { downloadExcelFormat( - { data: JSON.parse(JSON.stringify(excelDownList)) - , fileName: searchYear+'년 '+fcltyName + ' 수질현황' + { + data: JSON.parse(JSON.stringify(excelDownList)) + , fileName: searchYear + '년 ' + fcltyName + ' 수질현황' , sheetNameArray: [fcltyName + ' 수질현황'] , headerRange: 'A1:L1' - , decimalArray1: [1,2,3,6,7,8,11] - , decimalArray3: [4,5,9,10] + , decimalArray1: [1, 2, 3, 6, 7, 8, 11] + , decimalArray3: [4, 5, 9, 10] }); } else if (prevExcelListRef.current && prevExcelListRef.current.length > 0) { alert('다운받을 데이터가 없습니다.'); } prevExcelListRef.current = excelDownList; - }else{ + } else { // 총인설비 수질현황 const excelDownList: modalChoinginExcelType[] = []; - if(chongInWaterQltyYearList.length > 0){ - chongInWaterQltyYearList.forEach((item) => { + // if (chongInWaterQltyYearList.length > 0) { + // chongInWaterQltyYearList.forEach((item) => { + if (copyYearList.length > 0) { + copyYearList.forEach((item) => { excelDownList.push({ 날짜: item.msrDt, "유입BOD(mg/L)": CommaToNumber(item.tpInflWqltBod), "유입TP(mg/L)": CommaToNumber(item.tpInflWqltTp), "유출BOD(mg/L)": CommaToNumber(item.efflWqltBod), - "유출TP(mg/L)": CommaToNumber(item.efflWqltTn), + "유출TP(mg/L)": CommaToNumber(item.efflWqltTp), }); }); } if (excelDownList && excelDownList.length > 0) { downloadExcelFormat( - { data: JSON.parse(JSON.stringify(excelDownList)) - , fileName: searchYear+'년 '+fcltyName + ' 총인설비수질현황' + { + data: JSON.parse(JSON.stringify(excelDownList)) + , fileName: searchYear + '년 ' + fcltyName + ' 총인설비수질현황' , sheetNameArray: [fcltyName + ' 총인설비수질현황'] , headerRange: 'A1:E1' - , decimalArray1: [1,3] - , decimalArray3: [2,4] + , decimalArray1: [1, 3] + , decimalArray3: [2, 4] }); } else if (prevChoinginExcelListRef.current && prevChoinginExcelListRef.current.length > 0) { alert('다운받을 데이터가 없습니다.'); @@ -424,168 +422,127 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe return ( <> <UITab tabList={tabList1} defaultValue="tab_num1" onValueChange={(value) => setTabName(value)}> - <Button className={btnClass} style={{float: 'right'}} color="green" onClick={excelDownload}> + <Button className={btnClass} style={{ float: 'right' }} color="green" onClick={excelDownload}> {"엑셀다운로드"} </Button> {/* 처리시설 수질현황 */} <TabsContent value="tab_num1"> <UITab tabList={tabList2} defaultValue="tab_term_year"> - <TabsContent value="tab_term_year"> + <TabsContent value="tab_term_year">{/* 연간 */} <FacilityTermModalHeader<FacilityWaterQltyProps> searchList={facilityWaterQltyYearList} - fcltyCd={facilityCd} - fcltyName={fcltyName} - tabName={"tab_term_year"} - searchYear={searchYear} - ref={ref} - queryClick={filterData} + fcltyCd={facilityCd} fcltyName={fcltyName} tabName="tab_term_year" + searchYear={searchYear} ref={ref} queryClick={filterData} /> <div className="overflow-y-auto h-[350px]"> - <UiTable - headName={undefined} - tableData={copyYearList} - headlist={Number(searchYear)<COD_YEAR?upHeadListModalYearCod:upHeadListModalYear} - pageSize={0} - total={0} - cellClick={undefined} - onPageChange={() => {}} - className="h-[310px]" - boldArray={[0,1,2]} + <UiTable + headName={undefined} + tableData={copyYearList} + headlist={Number(searchYear) < COD_YEAR ? upHeadListModalYearCod : upHeadListModalYear} + pageSize={0} total={0} cellClick={undefined} + onPageChange={() => { }} className="h-[310px]" boldArray={[0, 1, 2]} > <HeadMakeColSpan upHeadList={upHeadListModalYear} layoutNum={3} /> </UiTable> </div> - <div className="card-header"> + <div className="card-header border-t mt-1"> <h3 className="text-2xl font-medium text-label">수질 TMS</h3> - {/*<h3 className="text-2xl font-medium text-label text-right">({msrmtDate?msrmtDate.substring(0,4)+'-'+msrmtDate.substring(4,6)+'-'+msrmtDate.substring(6,8):yesterDayStr})</h3> */} + <h3 className="text-2xl font-medium text-label text-right"> + ({msrmtDate ? msrmtDate.substring(0, 4) + '-' + msrmtDate.substring(4, 6) + '-' + msrmtDate.substring(6, 8) : yesterDayStr}) + </h3> </div> <div className="overflow-y-auto h-[250px]"> - <UiTable - headName={undefined} - tableData={tmsList} - headlist={upHeadListModalTms} - pageSize={0} - total={0} - cellClick={undefined} - onPageChange={() => {}} - boldArray={[0,1,2]} + <UiTable + headName={undefined} + tableData={tmsList} + headlist={upHeadListModalTms} + pageSize={0} total={0} cellClick={undefined} + onPageChange={() => { }} boldArray={[0, 1, 2]} > <HeadMakeColSpan upHeadList={upHeadListModalTms} layoutNum={4} /> </UiTable> - {msrmtDate != yesterDayStr?.replaceAll('-','') ? ( - <UnderComment commentArray={["전날 데이터 결측으로 인해 가장 최근 데이터 표출함"]} divHeight="0"/> + {msrmtDate != yesterDayStr?.replaceAll('-', '') ? ( + <UnderComment commentArray={["전날 데이터 결측으로 인해 가장 최근 데이터 표출함"]} divHeight="0" /> ) : null} </div> </TabsContent> - <TabsContent value="tab_term_summer"> + <TabsContent value="tab_term_summer">{/* 하절기 */} <FacilityTermModalHeader<FacilityWaterQltyProps> searchList={facilityWaterQltyYearList} - fcltyCd={facilityCd} - fcltyName={fcltyName} - tabName="tab_term_summer" - searchYear={searchYear} - ref={ref} - queryClick={filterData} + fcltyCd={facilityCd} fcltyName={fcltyName} tabName="tab_term_summer" + searchYear={searchYear} ref={ref} queryClick={filterData} /> <div className="overflow-y-auto h-[500px]"> - <UiTable + <UiTable headName={undefined} tableData={copyYearList} - headlist={Number(searchYear)<COD_YEAR?upHeadListModalYearCod:upHeadListModalYear} - pageSize={0} - total={0} - cellClick={undefined} - onPageChange={() => { } } - className="h-[500px]" - boldArray={[0,1,2]} + headlist={Number(searchYear) < COD_YEAR ? upHeadListModalYearCod : upHeadListModalYear} + pageSize={0} total={0} cellClick={undefined} + onPageChange={() => { }} className="h-[500px]" boldArray={[0, 1, 2]} > <HeadMakeColSpan upHeadList={upHeadListModalYear} layoutNum={3} /> </UiTable> </div> </TabsContent> - <TabsContent value="tab_term_winter"> + <TabsContent value="tab_term_winter">{/* 동절기 */} <FacilityTermModalHeader<FacilityWaterQltyProps> searchList={facilityWaterQltyYearList} - fcltyCd={facilityCd} - fcltyName={fcltyName} - tabName="tab_term_winter" - searchYear={searchYear} - ref={ref} - queryClick={filterData} + fcltyCd={facilityCd} fcltyName={fcltyName} tabName="tab_term_winter" + searchYear={searchYear} ref={ref} queryClick={filterData} /> <div className="overflow-y-auto h-[500px]"> - <UiTable - headName={undefined} - tableData={copyYearList} - headlist={Number(searchYear)<COD_YEAR?upHeadListModalYearCod:upHeadListModalYear} - pageSize={0} - total={0} - cellClick={undefined} - onPageChange={() => {}} - className="h-[500px]" - boldArray={[0,1,2]} + <UiTable + headName={undefined} + tableData={copyYearList} + headlist={Number(searchYear) < COD_YEAR ? upHeadListModalYearCod : upHeadListModalYear} + pageSize={0} total={0} cellClick={undefined} + onPageChange={() => { }} className="h-[500px]" boldArray={[0, 1, 2]} > <HeadMakeColSpan upHeadList={upHeadListModalYear} layoutNum={3} /> </UiTable> </div> </TabsContent> - <TabsContent value="tab_term_chuncheon"> + <TabsContent value="tab_term_chuncheon">{/* 청천시 */} <FacilityTermModalHeader<FacilityWaterQltyProps> searchList={facilityWaterQltyYearList} - fcltyCd={facilityCd} - fcltyName={fcltyName} - tabName="tab_term_chuncheon" - searchYear={searchYear} - ref={ref} - queryClick={filterData} + fcltyCd={facilityCd} fcltyName={fcltyName} tabName="tab_term_chuncheon" + searchYear={searchYear} ref={ref} queryClick={filterData} /> <div className="overflow-y-auto h-[500px]"> - <UiTable - headName={undefined} - tableData={copyYearList} - headlist={Number(searchYear)<COD_YEAR?upHeadListModalYearCod:upHeadListModalYear} - pageSize={0} - total={0} - cellClick={undefined} - onPageChange={() => {}} - className="h-[500px]" - boldArray={[0,1,2]} + <UiTable + headName={undefined} + tableData={copyYearList} + headlist={Number(searchYear) < COD_YEAR ? upHeadListModalYearCod : upHeadListModalYear} + pageSize={0} total={0} cellClick={undefined} + onPageChange={() => { }} className="h-[500px]" boldArray={[0, 1, 2]} > <HeadMakeColSpan upHeadList={upHeadListModalYear} layoutNum={3} /> </UiTable> </div> - <UnderComment commentArray={commentArrayChungchun} divHeight="10"/> + <UnderComment commentArray={commentArrayChungchun} divHeight="10" /> </TabsContent> - <TabsContent value="tab_term_rain"> + <TabsContent value="tab_term_rain">{/* 강우시 */} <FacilityTermModalHeader<FacilityWaterQltyProps> searchList={facilityWaterQltyYearList} - fcltyCd={facilityCd} - fcltyName={fcltyName} - tabName="tab_term_rain" - searchYear={searchYear} - ref={ref} - queryClick={filterData} + fcltyCd={facilityCd} fcltyName={fcltyName} tabName="tab_term_rain" + searchYear={searchYear} ref={ref} queryClick={filterData} /> <div className="overflow-y-auto h-[500px]"> - <UiTable - headName={undefined} - tableData={copyYearList} - headlist={Number(searchYear)<COD_YEAR?upHeadListModalYearCod:upHeadListModalYear} - pageSize={0} - total={0} - cellClick={undefined} - onPageChange={() => {}} - className="h-[500px]" - boldArray={[0,1,2]} + <UiTable + headName={undefined} + tableData={copyYearList} + headlist={Number(searchYear) < COD_YEAR ? upHeadListModalYearCod : upHeadListModalYear} + pageSize={0} total={0} cellClick={undefined} + onPageChange={() => { }} className="h-[500px]" boldArray={[0, 1, 2]} > <HeadMakeColSpan upHeadList={upHeadListModalYear} layoutNum={3} /> </UiTable> </div> - <UnderComment commentArray={commentArrayRain} divHeight="10"/> + <UnderComment commentArray={commentArrayRain} divHeight="10" /> </TabsContent> </UITab> </TabsContent> @@ -593,81 +550,57 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe {/* 총인설비 수질현황 */} <TabsContent value="tab_num2" > <UITab tabList={tabList3} defaultValue="tab_term_year_chongin"> - <TabsContent value="tab_term_year_chongin"> + <TabsContent value="tab_term_year_chongin">{/* 연간 */} <FacilityTermModalHeader<FacilityWaterQltyProps> searchList={chongInWaterQltyYearList} - fcltyCd={facilityCd} - fcltyName={fcltyName} - tabName="tab_term_year" - searchYear={searchYear} - ref={ref} - queryClick={filterData} + fcltyCd={facilityCd} fcltyName={fcltyName} tabName="tab_term_year" + searchYear={searchYear} ref={ref} queryClick={filterData} /> <div className="overflow-y-auto h-[550px]"> - <UiTable - headName={undefined} - tableData={copyYearList} - headlist={upHeadListModalChongIn} - pageSize={0} - total={0} - cellClick={undefined} - onPageChange={() => {}} - className="h-[500px]" - boldArray={[0,1,2]} - > - <HeadMakeColSpan upHeadList={upHeadListModalChongIn} layoutNum={5} /> - </UiTable> - </div> - </TabsContent> - - <TabsContent value="tab_term_summer_chongin"> - <FacilityTermModalHeader<FacilityWaterQltyProps> - searchList={chongInWaterQltyYearList} - fcltyCd={facilityCd} - fcltyName={fcltyName} - tabName="tab_term_summer" - searchYear={searchYear} - ref={ref} - queryClick={filterData} - /> - <div className="overflow-y-auto h-[550px]"> - <UiTable - headName={undefined} - tableData={copyYearList} - headlist={upHeadListModalChongIn} - pageSize={0} - total={0} - cellClick={undefined} - onPageChange={() => {}} - className="h-[500px]" - boldArray={[0,1,2]} - > - <HeadMakeColSpan upHeadList={upHeadListModalChongIn} layoutNum={5} /> - </UiTable> - </div> - </TabsContent> - - <TabsContent value="tab_term_winter_chongin"> - <FacilityTermModalHeader<FacilityWaterQltyProps> - searchList={chongInWaterQltyYearList} - fcltyCd={facilityCd} - fcltyName={fcltyName} - tabName="tab_term_winter" - searchYear={searchYear} - ref={ref} - queryClick={filterData} - /> - <div className="overflow-y-auto h-[550px]"> - <UiTable - headName={undefined} - tableData={copyYearList} + <UiTable + headName={undefined} + tableData={copyYearList} headlist={upHeadListModalChongIn} - pageSize={0} - total={0} - cellClick={undefined} - onPageChange={() => {}} - className="h-[500px]" - boldArray={[0,1,2]} + pageSize={0} total={0} cellClick={undefined} + onPageChange={() => { }} className="h-[500px]" boldArray={[0, 1, 2]} + > + <HeadMakeColSpan upHeadList={upHeadListModalChongIn} layoutNum={5} /> + </UiTable> + </div> + </TabsContent> + + <TabsContent value="tab_term_summer_chongin">{/* 하절기 */} + <FacilityTermModalHeader<FacilityWaterQltyProps> + searchList={chongInWaterQltyYearList} + fcltyCd={facilityCd} fcltyName={fcltyName} tabName="tab_term_summer" + searchYear={searchYear} ref={ref} queryClick={filterData} + /> + <div className="overflow-y-auto h-[550px]"> + <UiTable + headName={undefined} + tableData={copyYearList} + headlist={upHeadListModalChongIn} + pageSize={0} total={0} cellClick={undefined} + onPageChange={() => { }} className="h-[500px]" boldArray={[0, 1, 2]} + > + <HeadMakeColSpan upHeadList={upHeadListModalChongIn} layoutNum={5} /> + </UiTable> + </div> + </TabsContent> + + <TabsContent value="tab_term_winter_chongin">{/* 동절기 */} + <FacilityTermModalHeader<FacilityWaterQltyProps> + searchList={chongInWaterQltyYearList} + fcltyCd={facilityCd} fcltyName={fcltyName} tabName="tab_term_winter" + searchYear={searchYear} ref={ref} queryClick={filterData} + /> + <div className="overflow-y-auto h-[550px]"> + <UiTable + headName={undefined} + tableData={copyYearList} + headlist={upHeadListModalChongIn} + pageSize={0} total={0} cellClick={undefined} + onPageChange={() => { }} className="h-[500px]" boldArray={[0, 1, 2]} > <HeadMakeColSpan upHeadList={upHeadListModalChongIn} layoutNum={5} /> </UiTable> @@ -678,4 +611,4 @@ export default function WaterQualityModal({fcltyName='', facilityCd='', searchYe </UITab> </> ); -} \ No newline at end of file +} diff --git a/apps/fo/app/pages/(with-layout)/facilityWaterQlty/page.tsx b/apps/fo/app/pages/(with-layout)/facilityWaterQlty/page.tsx index 885ee2728..a90929225 100644 --- a/apps/fo/app/pages/(with-layout)/facilityWaterQlty/page.tsx +++ b/apps/fo/app/pages/(with-layout)/facilityWaterQlty/page.tsx @@ -6,13 +6,13 @@ "use client" -import { useFoStore } from "@/app/store"; +import { StoreContext, useFoStore } from "@/app/store"; import type { TableUpperProps } from "@/app/store/common"; import type { FacilityWaterQltyProps } from "@/app/store/facilityWaterQlty"; import { ContentsDiv, downloadExcelFormat, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components"; import ListDiv from "@common/business_components/ui/list_div"; import LoadingOverlay from "@common/business_components/ui/loadingBar"; -import { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useContext, useEffect, useRef, useState } from "react"; import WaterQualityModal from "./modal/page"; export default function ProcessFacilitySearch() { @@ -21,9 +21,14 @@ export default function ProcessFacilitySearch() { , initializeCommonData , searchFacilityWaterQltyList } = useFoStore((state) => state) || { FacilityWaterQltyList: { facilityWaterQltyList: [], facilityWaterQltyCount: 0 } }; + const store = useContext(StoreContext); + + if (!store) { + throw new Error("StoreContext is not available. Ensure StoreProvider is mounted."); + } const { - isInit, + isInit, selectUpdownData, selectOperationData, selectSidoData, @@ -36,6 +41,11 @@ export default function ProcessFacilitySearch() { btnClass } = Common; + const defaultSearchYear = + selectSearchYear.find((item) => item.selected)?.val || + selectSearchYear[0]?.val || + "0"; + const { mainLabelArray, upHeadList1, @@ -43,13 +53,13 @@ export default function ProcessFacilitySearch() { upHeadList3, facilityWaterQltyList, facilityWaterQltyCount, - excelList, isLoading, } = FacilityWaterQltyMain; const mainList: FacilityWaterQltyProps[] = facilityWaterQltyList || []; const [initTrigger, setInitTrigger] = useState<boolean>(false); - const [excelTrigger, setExcelTrigger] = useState<boolean>(false); + // 엑셀다운로드 트리거 + const [isExcelDownClick, setIsExcelDownClick] = useState<boolean>(false); // 그리드 관련 const [resetPage, setResetPage] = useState<boolean>(false); const searchInputRef = useRef<HTMLDivElement & HTMLInputElement>(null); @@ -57,7 +67,7 @@ export default function ProcessFacilitySearch() { const [upheadList, setUpheadList] = useState<TableUpperProps[]>(upHeadList1); const [fcltyName, setFcltyName] = useState<string>(""); const [operationValue, setOperationValue] = useState<string>("0"); - const [searchYearValue, setSearchYearValue] = useState<string>(selectSearchYear[0]?.val || "0"); + const [searchYearValue, setSearchYearValue] = useState<string>(defaultSearchYear); const [fcltCpct, setFcltCpct] = useState<string>("0"); // 모달 상태 추가 @@ -66,27 +76,27 @@ export default function ProcessFacilitySearch() { const [fcltyCd, setFcltyCd] = useState<string>(""); const [modalYear, setModalYear] = useState<string>(""); - const searchFcltyQltyList = useCallback(({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=20) => { + const searchFcltyQltyList = useCallback(async ({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt: number = 20) => { let nameCd, partStr; switch (partValue) { case 'R': if (regionValue !== '0') { - if(unitWaterShedValue==='0') nameCd = regionValue; - else if (middleRegionValue==='0') nameCd = unitWaterShedValue; + if (unitWaterShedValue === '0') nameCd = regionValue; + else if (middleRegionValue === '0') nameCd = unitWaterShedValue; else nameCd = middleRegionValue; } partStr = partValue break; default: - if(sidoValue !== '0') nameCd = ref.current.sidoValue!="0"?ref.current.sidoValue:sidoValue; - if(sigunValue !== '0') nameCd = sigunValue; - partStr = envPartValue !== '0' ? envPartValue:partValue; - break; + if (sidoValue !== '0') nameCd = ref.current.sidoValue != "0" ? ref.current.sidoValue : sidoValue; + if (sigunValue !== '0') nameCd = sigunValue; + partStr = envPartValue !== '0' ? envPartValue : partValue; + break; } - if(partStr ==='G'){ - searchFacilityWaterQltyList(pageNo, pageCnt, searchYearValue); - }else{ - searchFacilityWaterQltyList(pageNo, pageCnt, searchYearValue, partStr, nameCd || '0', fcltCpct, operationValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined); + if (partStr === 'G') { + await searchFacilityWaterQltyList(pageNo, pageCnt, searchYearValue); + } else { + await searchFacilityWaterQltyList(pageNo, pageCnt, searchYearValue, partStr, nameCd || '0', fcltCpct, operationValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined); } }, [operationValue, fcltyName, searchFacilityWaterQltyList, searchYearValue, fcltCpct]); @@ -96,7 +106,7 @@ export default function ProcessFacilitySearch() { // 상태값 설정 setFcltCpct(selectUpdownData[0]?.val || "0"); setOperationValue(selectOperationData[0]?.val || "0"); - setSearchYearValue(selectSearchYear[0]?.val || "0"); + setSearchYearValue(defaultSearchYear); // SearchInputSelect 초기화 setFcltyName(""); if (searchInputRef.current) { @@ -138,42 +148,60 @@ export default function ProcessFacilitySearch() { useEffect(() => { if (!isInit) { - if(!initTrigger){ + if (!initTrigger) { initializeCommonData(); searchFcltyQltyList(ref.current, 1, 10); - setInitTrigger(true); + setInitTrigger(true); } } }, [isInit, initializeCommonData, searchFacilityWaterQltyList, searchFcltyQltyList, initTrigger]); - useEffect(() => { - if(!excelTrigger) return; - - if (excelList && excelList.length > 0) { - downloadExcelFormat( - { - data: excelList as unknown as JSON[], - fileName: '시설별 수질현황', - sheetNameArray: ['시설별 수질현황'], - headerRange: 'A1:T1', - decimalArray1:[10,11,12,15,16,17], - decimalArray3:[13,14,18,19] - } - ); - } - }, [excelList, excelTrigger]); - - const chkVal = useCallback((returntype:ReturnType) => { + const chkVal = useCallback((returntype: ReturnType) => { // setWidthSize(returntype.partValue=='A' ?"w-[120%]":"w-[130%]") - setUpheadList(returntype.partValue == 'R' ? upHeadList2 : returntype.partValue == 'G'?upHeadList3:upHeadList1); + setUpheadList(returntype.partValue == 'R' ? upHeadList2 : returntype.partValue == 'G' ? upHeadList3 : upHeadList1); setResetPage(true); }, [upHeadList1, upHeadList2, upHeadList3]); - const excelDownload = useCallback(() => { - setExcelTrigger(true); - searchFcltyQltyList(ref.current, 1, 10000); - }, [searchFcltyQltyList]); - + const excelDownload = useCallback(async () => { + if (isExcelDownClick) return; + if (ref.current.partValue === 'A') { // 행정별 + if (!ref.current.sidoValue || ref.current.sidoValue === '0') { + alert('엑셀 다운로드는 시도 조건을 선택한 후 이용 가능합니다.'); + return; + } + } else if (ref.current.partValue === 'R') { // 유역별 + if (!ref.current.regionValue || ref.current.regionValue === '0') { + alert('엑셀 다운로드는 유역 조건을 선택한 후 이용 가능합니다.'); + return; + } + } else if (ref.current.partValue === 'G') { // 환경청별 + if (!ref.current.envPartValue || ref.current.envPartValue === '0') { + alert('엑셀 다운로드는 환경청 조건을 선택한 후 이용 가능합니다.'); + return; + } + } + setIsExcelDownClick(true); + try { + await searchFcltyQltyList(ref.current, 1, 10000); + const latestExcelList = store.getState().FacilityWaterQltyMain.excelList || []; + if (latestExcelList.length > 0) { + downloadExcelFormat( + { + data: latestExcelList as unknown as JSON[], + fileName: '시설별 수질현황', + sheetNameArray: ['시설별 수질현황'], + headerRange: 'A1:T1', + decimalArray0: [9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] + } + ); + } else { + alert("조회된 결과가 없습니다."); + } + } finally { + setIsExcelDownClick(false); + } + }, [isExcelDownClick, searchFcltyQltyList, store]); + const handlePageChange = useCallback((page: number) => { setResetPage(false); searchFcltyQltyList(ref.current, page, 10); @@ -201,8 +229,9 @@ export default function ProcessFacilitySearch() { btnClass={btnClass} queryClick={chkVal} excelClick={excelDownload} + excelDisabled={isExcelDownClick} initClick={resetVal} - ref={ref} isAdmin={false} initSidoValue={"11"}/> + ref={ref} isAdmin={false} initSidoValue={"11"} /> <ListDiv> <LoadingOverlay isLoading={isLoading} /> <div className="h-full w-full overflow-x-scroll min-[1200px]:overflow-x-hidden"> @@ -215,7 +244,7 @@ export default function ProcessFacilitySearch() { {selectedRow !== null && ( <UiModal body={<WaterQualityModal fcltyName={fcltyNm} facilityCd={fcltyCd} searchYear={modalYear}></WaterQualityModal>} - bodyClass="sm:max-w-[90vw] w-[1300px] h-[920px]" + bodyClass="sm:max-w-[90vw] w-[1300px] h-[100dvh] max-h-[930px] overflow-y-auto" open={selectedRow !== null} onOpenChange={(open) => !open && closeModal()} noHeaderYn="Y" @@ -224,4 +253,4 @@ export default function ProcessFacilitySearch() { )} </MainDiv> ); -} \ No newline at end of file +} diff --git a/apps/fo/app/pages/(with-layout)/flowRate/modal/page.tsx b/apps/fo/app/pages/(with-layout)/flowRate/modal/page.tsx index 42512f802..0cffa775e 100644 --- a/apps/fo/app/pages/(with-layout)/flowRate/modal/page.tsx +++ b/apps/fo/app/pages/(with-layout)/flowRate/modal/page.tsx @@ -8,7 +8,8 @@ import FacilityTermModalHeader from "@/app/components/common/FacilityTermModalHeader"; import { useFoStore } from "@/app/store"; import type { FlowRateModalInterface, modalExcelType } from "@/app/store/flowRate"; -import { downloadExcel, UITab, UiTable } from "@common/business_components/ui"; +import { dataFormSetting } from "@/app/store/flowRate"; +import { downloadExcelFormat, UITab, UiTable } from "@common/business_components/ui"; import { Button, TabsContent } from "@common/components/ui"; import { useCallback, useEffect, useRef, useState } from "react"; import type { ReturnType } from "../../../../components/common/FacilityTermModalHeader"; @@ -33,12 +34,10 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st const { btnClass } = Common; const [copyYearList, setCopyYearList] = useState<FlowRateModalInterface[]>([]); const [isLoading, setIsLoading] = useState(true); + const [activeTab, setActiveTab] = useState<string>("tab_term_year"); const prevExcelListRef = useRef<modalExcelType[] | null>(null); - const { - modalFlowRateListYear, - upHeadListFlowRateModalYear, - } = FlowRateModal; - + const { modalFlowRateListYear, upHeadListFlowRateModalYear, } = FlowRateModal; + const ref = useRef<ReturnType<FlowRateModalInterface>>({ startDate: new Date(), endDate: new Date(), @@ -52,11 +51,11 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st const filteredData = setFilterAfterData(returnType.filteredList as FlowRateModalInterface[]); setCopyYearList(filteredData); }, []); - + // 필터링 된 후 최대, 최소, 평균 값 계산 const setFilterAfterData = (filteredData: FlowRateModalInterface[]) => { if (!filteredData || filteredData.length === 0) return []; - + // 쉼표가 포함된 문자열을 숫자로 변환하는 함수 const parseNumber = (value: string | number | null | undefined): number => { if (value === null || value === undefined || value === '') return 0; @@ -79,7 +78,7 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st let minreturnAmnt = parseNumber(filteredData[0]?.returnAmnt); let minefflAmnt = parseNumber(filteredData[0]?.efflAmnt); let mininfoRnDay = parseNumber(filteredData[0]?.infoRnDay && filteredData[0]?.infoRnDay.toString().trim() !== 'x' ? filteredData[0]?.infoRnDay.toString().trim() : 0); - let minNoProcessInflAmnt = 99999999; + let minNoProcessInflAmnt = 99999999; // 미처리하수 추정량 let avgtotInflAmnt = 0; let avginflAmnt = 0; @@ -89,7 +88,7 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st let avginfoRnDay = 0; let avgNoProcessInflAmnt = 0; let noProcessInflAmntCnt = 0; - + filteredData.forEach((item: FlowRateModalInterface) => { const totInflAmnt = parseNumber(item.totInflAmnt); const inflAmnt = parseNumber(item.inflAmnt); @@ -119,24 +118,30 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st avgefflAmnt += efflAmnt; avginfoRnDay += infoRnDay; - if(item.noProcessInflAmnt && item.noProcessInflAmnt.toString().trim() != '' && item.noProcessInflAmnt != undefined){ - const noProcessInflAmnt = Number(item.noProcessInflAmnt.toString().replaceAll(',','')); + if (item.noProcessInflAmnt && item.noProcessInflAmnt.toString().trim() != '' && item.noProcessInflAmnt != undefined) { + const noProcessInflAmnt = Number(item.noProcessInflAmnt.toString().replaceAll(',', '')); avgNoProcessInflAmnt += noProcessInflAmnt; maxNoProcessInflAmnt = Math.max(maxNoProcessInflAmnt, noProcessInflAmnt); minNoProcessInflAmnt = Math.min(minNoProcessInflAmnt, noProcessInflAmnt); - noProcessInflAmntCnt +=1; + noProcessInflAmntCnt += 1; } }); + const formattedData = filteredData.map((item: FlowRateModalInterface) => { + const newItem = { ...item }; + dataFormSetting(newItem); + return newItem; + }); + const newFilteredData = [ { msrDt: '평균', - totInflAmnt: avgtotInflAmnt && avgtotInflAmnt!=0 ? roundUp((avgtotInflAmnt / filteredData.length), 0) : 0, - inflAmnt: avginflAmnt && avginflAmnt!=0 ? roundUp((avginflAmnt / filteredData.length), 0) : 0, - sumFlow: avgsumFlow && avgsumFlow!=0 ? roundUp((avgsumFlow / filteredData.length), 0) : 0, - returnAmnt: avgreturnAmnt && avgreturnAmnt!=0 ? roundUp((avgreturnAmnt / filteredData.length), 0) : 0, - efflAmnt: avgefflAmnt && avgefflAmnt!=0 ? roundUp((avgefflAmnt / filteredData.length), 0) : 0, - noProcessInflAmnt : roundNumber(avgNoProcessInflAmnt / noProcessInflAmntCnt, 0), + totInflAmnt: avgtotInflAmnt && avgtotInflAmnt != 0 ? roundUp((avgtotInflAmnt / filteredData.length), 0) : 0, + inflAmnt: avginflAmnt && avginflAmnt != 0 ? roundUp((avginflAmnt / filteredData.length), 0) : 0, + sumFlow: avgsumFlow && avgsumFlow != 0 ? roundUp((avgsumFlow / filteredData.length), 0) : 0, + returnAmnt: avgreturnAmnt && avgreturnAmnt != 0 ? roundUp((avgreturnAmnt / filteredData.length), 0) : 0, + efflAmnt: avgefflAmnt && avgefflAmnt != 0 ? roundUp((avgefflAmnt / filteredData.length), 0) : 0, + noProcessInflAmnt: roundNumber(avgNoProcessInflAmnt / noProcessInflAmntCnt, 0), infoRnDay: avginfoRnDay > 0 ? roundUp((avginfoRnDay / filteredData.length), 1) : '0.0', rainEffectDay: 0, }, @@ -147,7 +152,7 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st sumFlow: roundUp(maxsumFlow, 0), returnAmnt: roundUp(maxreturnAmnt, 0), efflAmnt: roundUp(maxefflAmnt, 0), - noProcessInflAmnt : roundNumber(maxNoProcessInflAmnt, 0), + noProcessInflAmnt: roundNumber(maxNoProcessInflAmnt, 0), infoRnDay: maxinfoRnDay > 0 ? roundUp(maxinfoRnDay, 1) : '0.0', rainEffectDay: 0, }, @@ -158,11 +163,11 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st sumFlow: roundUp(minsumFlow, 0), returnAmnt: roundUp(minreturnAmnt, 0), efflAmnt: roundUp(minefflAmnt, 0), - noProcessInflAmnt : roundNumber(minNoProcessInflAmnt, 0), + noProcessInflAmnt: roundNumber(minNoProcessInflAmnt, 0), infoRnDay: mininfoRnDay > 0 ? roundUp(mininfoRnDay, 1) : '0.0', rainEffectDay: 0, }, - ...filteredData + ...formattedData ]; return newFilteredData; } @@ -179,13 +184,9 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st fetchData(); }, [props.facilityCd, props.searchYear, searchFlowRateModalList]); - if (isLoading) { - return <div>로딩 중...</div>; - } - const excelDownload = () => { const excelDownList: modalExcelType[] = []; - if(copyYearList.length > 0){ + if (copyYearList.length > 0) { copyYearList.forEach((item) => { excelDownList.push({ "날짜": item.msrDt, @@ -200,20 +201,58 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st }); } if (excelDownList && excelDownList.length > 0) { - downloadExcel({ data: JSON.parse(JSON.stringify(excelDownList)), fileName: props.searchYear+'년 '+props.fcltyName + ' 유량현황', sheetNameArray: [props.fcltyName + ' 유량현황'], headerRange: 'A1:H1' }); + downloadExcelFormat({ + data: JSON.parse(JSON.stringify(excelDownList)), + fileName: props.searchYear + '년 ' + props.fcltyName + ' 유량현황', + sheetNameArray: [props.fcltyName + ' 유량현황'], + headerRange: 'A1:H1', + decimalArray0: [1, 2, 3, 4, 5, 6], + decimalArray1: [0], + }); } else if (prevExcelListRef.current && prevExcelListRef.current.length > 0) { - alert('다운받을 데이터가 없습니다.'); + alert('조회된 결과가 없습니다.'); } prevExcelListRef.current = excelDownList; } + // const rainCellClassName = useCallback( + // (row: FlowRateModalInterface, headId: string, rowIndex: number) => { + // if (activeTab !== "tab_term_rain") return ""; + // if (headId !== "infoRnDay") return ""; + // if (rowIndex < 3) return ""; + + // const raw = row.infoRnDay; + // const value = typeof raw === "string" ? Number(raw.replace(/,/g, "").trim()) : Number(raw); + // if (!Number.isFinite(value)) return ""; + // return value > 3 ? "bg-red-50 font-semibold" : ""; + // }, + // [activeTab], + // ); + + const rainRowClassName = useCallback( + (row: FlowRateModalInterface, rowIndex: number) => { + if (activeTab !== "tab_term_rain") return ""; + if (rowIndex < 3) return ""; + + const raw = row.infoRnDay; + const value = typeof raw === "string" ? Number(raw.replace(/,/g, "").trim()) : Number(raw); + if (!Number.isFinite(value)) return ""; + return value > 3 ? "bg-slate-50" : ""; + }, + [activeTab], + ); + + if (isLoading) { + return <div>로딩 중...</div>; + } + return ( <> - <UITab tabList={tabList} defaultValue="tab_term_year"> - <Button className={btnClass} style={{float: 'right'}} color="green" onClick={excelDownload}> + <UITab tabList={tabList} defaultValue="tab_term_year" onValueChange={setActiveTab}> + <Button className={btnClass} style={{ float: 'right' }} color="green" onClick={excelDownload}> {"엑셀다운로드"} </Button> - <TabsContent value="tab_term_year"> + <TabsContent value="tab_term_year"> {/* 연간 */} <FacilityTermModalHeader<FlowRateModalInterface> searchList={modalFlowRateListYear} fcltyCd={props.facilityCd} @@ -224,23 +263,23 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st queryClick={filterData} /> <div className="h-[370px] overflow-y-auto overflow-x-hidden"> - <UiTable - headName="유량현황" - tableData={copyYearList} - headlist={upHeadListFlowRateModalYear} - pageSize={0} - total={0} - cellClick={undefined} - onPageChange={() => {}} - className="h-[370px]" - boldArray={[0,1,2]} + <UiTable + headName="유량현황" + tableData={copyYearList} + headlist={upHeadListFlowRateModalYear} + pageSize={0} + total={0} + cellClick={undefined} + onPageChange={() => { }} + className="h-[370px]" + boldArray={[0, 1, 2]} /> </div> <div className="mt-20"> <UnderComment commentArray={[commentArray]} /> </div> </TabsContent> - <TabsContent value="tab_term_summer"> + <TabsContent value="tab_term_summer"> {/* 하절기 */} <FacilityTermModalHeader<FlowRateModalInterface> searchList={modalFlowRateListYear} fcltyCd={props.facilityCd} @@ -251,23 +290,23 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st queryClick={filterData} /> <div className="h-[370px] overflow-y-auto overflow-x-hidden"> - <UiTable - headName="유량현황" - tableData={copyYearList} - headlist={upHeadListFlowRateModalYear} - pageSize={0} - total={0} - cellClick={undefined} - onPageChange={() => {}} - className="h-[370px]" - boldArray={[0,1,2]} + <UiTable + headName="유량현황" + tableData={copyYearList} + headlist={upHeadListFlowRateModalYear} + pageSize={0} + total={0} + cellClick={undefined} + onPageChange={() => { }} + className="h-[370px]" + boldArray={[0, 1, 2]} /> </div> <div className="mt-20"> <UnderComment commentArray={[commentArray]} /> </div> </TabsContent> - <TabsContent value="tab_term_winter"> + <TabsContent value="tab_term_winter"> {/* 동절기 */} <FacilityTermModalHeader<FlowRateModalInterface> searchList={modalFlowRateListYear} fcltyCd={props.facilityCd} @@ -278,23 +317,23 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st queryClick={filterData} /> <div className="h-[370px] overflow-y-auto overflow-x-hidden"> - <UiTable - headName="유량현황" - tableData={copyYearList} - headlist={upHeadListFlowRateModalYear} - pageSize={0} - total={0} - cellClick={undefined} - onPageChange={() => {}} - className="h-[370px]" - boldArray={[0,1,2]} + <UiTable + headName="유량현황" + tableData={copyYearList} + headlist={upHeadListFlowRateModalYear} + pageSize={0} + total={0} + cellClick={undefined} + onPageChange={() => { }} + className="h-[370px]" + boldArray={[0, 1, 2]} /> </div> <div className="mt-20"> <UnderComment commentArray={[commentArray]} /> </div> </TabsContent> - <TabsContent value="tab_term_chuncheon"> + <TabsContent value="tab_term_chuncheon"> {/* 청천시 */} <FacilityTermModalHeader<FlowRateModalInterface> searchList={modalFlowRateListYear} fcltyCd={props.facilityCd} @@ -305,23 +344,23 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st queryClick={filterData} /> <div className="h-[370px] overflow-y-auto overflow-x-hidden"> - <UiTable - headName="유량현황" - tableData={copyYearList} - headlist={upHeadListFlowRateModalYear} - pageSize={0} - total={0} - cellClick={undefined} - onPageChange={() => {}} - className="h-[370px]" - boldArray={[0,1,2]} + <UiTable + headName="유량현황" + tableData={copyYearList} + headlist={upHeadListFlowRateModalYear} + pageSize={0} + total={0} + cellClick={undefined} + onPageChange={() => { }} + className="h-[370px]" + boldArray={[0, 1, 2]} /> </div> <div className="mt-20"> <UnderComment commentArray={[commentArray]} /> </div> </TabsContent> - <TabsContent value="tab_term_rain"> + <TabsContent value="tab_term_rain"> {/* 강우시 */} <FacilityTermModalHeader<FlowRateModalInterface> searchList={modalFlowRateListYear} fcltyCd={props.facilityCd} @@ -332,13 +371,15 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st queryClick={filterData} /> <div className="h-[370px] overflow-y-auto overflow-x-hidden"> - <UiTable headName="유량현황" tableData={copyYearList} headlist={upHeadListFlowRateModalYear} pageSize={0} total={0} cellClick={undefined} onPageChange={() => {}} className="h-[370px]" boldArray={[0,1,2]}/> + <UiTable headName="유량현황" tableData={copyYearList} headlist={upHeadListFlowRateModalYear} pageSize={0} total={0} + cellClick={undefined} onPageChange={() => { }} className="h-[370px]" boldArray={[0, 1, 2]} + rowClassName={rainRowClassName} /> </div> <div className="mt-20"> - <UnderComment commentArray={["미처리하수 추정량 산식 =[(강우시 일별유입유량*강우시 일별유입BOD)-(청천시 연평균유입유량*청천시 연평균유입BOD)]/강우시 일별유입BOD)","강우량 3mm 이상만 날과 강우영향일(강우일+2일)의 날"]} /> + <UnderComment commentArray={["미처리하수 추정량 산식 =[(강우시 일별유입유량*강우시 일별유입BOD)-(청천시 연평균유입유량*청천시 연평균유입BOD)]/강우시 일별유입BOD)", "강우량 3mm 이상만 날과 강우영향일(강우일+2일)의 날"]} /> </div> </TabsContent> </UITab> </> ); -} \ No newline at end of file +} diff --git a/apps/fo/app/pages/(with-layout)/flowRate/page.tsx b/apps/fo/app/pages/(with-layout)/flowRate/page.tsx index 77d96fff6..d83386dee 100644 --- a/apps/fo/app/pages/(with-layout)/flowRate/page.tsx +++ b/apps/fo/app/pages/(with-layout)/flowRate/page.tsx @@ -5,13 +5,13 @@ */ "use client" -import { useFoStore } from "@/app/store"; +import { StoreContext, useFoStore } from "@/app/store"; import type { TableUpperProps } from "@/app/store/common"; -import type { ExcelType } from "@/app/store/flowRate"; +// import type { ExcelType } from "@/app/store/flowRate"; import { ContentsDiv, downloadExcelFormat, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components"; import ListDiv from "@common/business_components/ui/list_div"; import LoadingOverlay from "@common/business_components/ui/loadingBar"; -import { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useContext, useEffect, useRef, useState } from "react"; import FlowRateModal from "./modal/page"; export default function ProcessFacilitySearch() { @@ -21,6 +21,11 @@ export default function ProcessFacilitySearch() { ,initializeCommonData ,searchFlowRateList } = useFoStore((state) => state); + const store = useContext(StoreContext); + + if (!store) { + throw new Error("StoreContext is not available. Ensure StoreProvider is mounted."); + } const searchInputRef = useRef<HTMLDivElement & HTMLInputElement>(null); @@ -38,6 +43,11 @@ export default function ProcessFacilitySearch() { btnClass } = Common; + const defaultSearchYear = + selectSearchYear.find((item) => item.selected)?.val || + selectSearchYear[0]?.val || + "0"; + const { flowRateSearchLabelArray } = FlowRateSearch; @@ -49,7 +59,6 @@ export default function ProcessFacilitySearch() { upHeadList3, flowRateList, flowRateCount, - excelList, isLoading, } = FlowRateList; @@ -57,20 +66,20 @@ export default function ProcessFacilitySearch() { const [fcltCpct, setFcltCpct] = useState<string>("0"); const [fcltyName, setFcltyName] = useState<string>(""); const [operationValue, setOperationValue] = useState<string>("0"); - const [searchYearValue, setSearchYearValue] = useState<string>(selectSearchYear[0]?.val || "0"); + const [searchYearValue, setSearchYearValue] = useState<string>(defaultSearchYear); // const [widthSize, setWidthSize] = useState<string>("w-[120%]"); const [upheadList, setUpheadList] = useState<TableUpperProps[]>(upHeadList1); const [resetPage, setResetPage] = useState<boolean>(false); - const prevExcelListRef = useRef<ExcelType[] | null>(null); + // const prevExcelListRef = useRef<ExcelType[] | null>(null); // 모달 상태 추가 const [selectedRow, setSelectedRow] = useState<number | null>(null); const [fcltyNm, setFcltyNm] = useState<string>(""); const [fcltyCd, setFcltyCd] = useState<string>(""); - const [excelTrigger, setExcelTrigger] = useState<boolean>(false); + const [isExcelDownClick, setIsExcelDownClick] = useState<boolean>(false); const [initTrigger, setInitTrigger] = useState<boolean>(false); - const searchFlowRate = useCallback(({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=20) => { + const searchFlowRate = useCallback(async ({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=20) => { let nameCd, partStr; switch (partValue) { @@ -89,9 +98,9 @@ export default function ProcessFacilitySearch() { break; } if(partStr==='G'){ - searchFlowRateList(pageNo, pageCnt, searchYearValue); + await searchFlowRateList(pageNo, pageCnt, searchYearValue); }else{ - searchFlowRateList(pageNo, pageCnt, searchYearValue, partStr, nameCd || '0', fcltCpct, operationValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined); + await searchFlowRateList(pageNo, pageCnt, searchYearValue, partStr, nameCd || '0', fcltCpct, operationValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined); } }, [searchFlowRateList, searchYearValue, fcltCpct, operationValue, fcltyName]); @@ -99,7 +108,7 @@ export default function ProcessFacilitySearch() { // setWidthSize("w-[120%]"); setUpheadList(upHeadList1) // 상태값 설정 - setSearchYearValue(selectSearchYear[0]?.val || "0"); + setSearchYearValue(defaultSearchYear); setFcltCpct(selectUpdownData[0]?.val || "0"); setOperationValue(selectOperationData[0]?.val || "0"); // SearchInputSelect 초기화 @@ -146,17 +155,6 @@ export default function ProcessFacilitySearch() { } }, [isInit, initializeCommonData, searchFlowRateList, selectSearchYear, searchFlowRate, initTrigger]); - useEffect(() => { - if(!excelTrigger) return; - - if (excelList && excelList.length > 0) { - downloadExcelFormat({ data: JSON.parse(JSON.stringify(excelList)), fileName: '시설별 유량현황', sheetNameArray: ['시설별 유량현황'], headerRange: 'A1:O1', decimalArray0: [9,10,11,12,13,14] }); - } else if (prevExcelListRef.current && prevExcelListRef.current.length > 0) { - alert('다운받을 데이터가 없습니다.'); - } - prevExcelListRef.current = excelList; - }, [excelList, excelTrigger]); - const ref = useRef<ReturnType>({ partValue: '', sidoValue: '11', @@ -175,10 +173,37 @@ export default function ProcessFacilitySearch() { setResetPage(true); }, [upHeadList1, upHeadList2, upHeadList3]); - const excelDownload = useCallback(() => { - searchFlowRate(ref.current, 1, 10000); - setExcelTrigger(true); - }, [searchFlowRate]); + const excelDownload = useCallback(async () => { + if (isExcelDownClick) return; + if (ref.current.partValue === 'A') { // 행정별 + if (!ref.current.sidoValue || ref.current.sidoValue === '0') { + alert('엑셀 다운로드는 시도 조건을 선택한 후 이용 가능합니다.'); + return; + } + } else if (ref.current.partValue === 'R') { // 유역별 + if (!ref.current.regionValue || ref.current.regionValue === '0') { + alert('엑셀 다운로드는 유역 조건을 선택한 후 이용 가능합니다.'); + return; + } + } else if (ref.current.partValue === 'G') { // 환경청별 + if (!ref.current.envPartValue || ref.current.envPartValue === '0') { + alert('엑셀 다운로드는 환경청 조건을 선택한 후 이용 가능합니다.'); + return; + } + } + setIsExcelDownClick(true); + try { + await searchFlowRate(ref.current, 1, 10000); + const latestExcelList = store.getState().FlowRateList.excelList || []; + if (latestExcelList.length > 0) { + downloadExcelFormat({ data: JSON.parse(JSON.stringify(latestExcelList)), fileName: '시설별 유량현황', sheetNameArray: ['시설별 유량현황'], headerRange: 'A1:O1', decimalArray0: [9,10,11,12,13,14] }); + } else { + alert('조회된 결과가 없습니다.'); + } + } finally { + setIsExcelDownClick(false); + } + }, [isExcelDownClick, searchFlowRate, store]); const total = flowRateCount; return ( @@ -203,6 +228,7 @@ export default function ProcessFacilitySearch() { queryClick={chkVal} excelClick={excelDownload} initClick={resetVal} + excelDisabled={isExcelDownClick} ref={ref} isAdmin={false} initSidoValue={'11'}/> <ListDiv> <LoadingOverlay isLoading={isLoading} /> @@ -216,7 +242,7 @@ export default function ProcessFacilitySearch() { {selectedRow !== null && ( <UiModal body={<FlowRateModal fcltyName={fcltyNm} facilityCd={fcltyCd} searchYear={searchYearValue}></FlowRateModal>} - bodyClass="sm:max-w-[90vw] w-[1250px] h-[780px]" + bodyClass="sm:max-w-[90vw] w-[1250px] h-[100dvh] max-h-[780px]" open={selectedRow !== null} onOpenChange={(open: boolean) => !open && closeModal()} /> diff --git a/apps/fo/app/pages/(with-layout)/planSewage/page.tsx b/apps/fo/app/pages/(with-layout)/planSewage/page.tsx index 1f8bd1e94..bdfd9c4b4 100644 --- a/apps/fo/app/pages/(with-layout)/planSewage/page.tsx +++ b/apps/fo/app/pages/(with-layout)/planSewage/page.tsx @@ -5,11 +5,11 @@ */ "use client" -import { useFoStore } from "@/app/store"; +import { StoreContext, useFoStore } from "@/app/store"; import type { TableUpperProps } from "@/app/store/common"; -import { downloadExcel, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiTable, type ReturnType } from "@common/business_components"; +import { downloadExcelFormat, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiTable, type ReturnType } from "@common/business_components"; import ListDiv from "@common/business_components/ui/list_div"; -import { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useContext, useEffect, useRef, useState } from "react"; import LoadingOverlay from "@common/business_components/ui/loadingBar"; import UnderComment from "@/app/components/common/UnderComment"; @@ -20,13 +20,18 @@ const commentArray = [ ] export default function PlanSewage() { - const {Common - ,PlanSewage - ,PlanSewageSearch - ,initializeCommonData - ,searchPlanSewageList - } = useFoStore((state) => state) || { processAreaList: { processAreaList: [] }}; - + const { Common + , PlanSewage + , PlanSewageSearch + , initializeCommonData + , searchPlanSewageList + } = useFoStore((state) => state) || { processAreaList: { processAreaList: [] } }; + const store = useContext(StoreContext); + + if (!store) { + throw new Error("StoreContext is not available. Ensure StoreProvider is mounted."); + } + const { isInit, selectUpdownData, @@ -41,8 +46,13 @@ export default function PlanSewage() { btnClass } = Common; + const defaultSearchYear = + selectSearchYear.find((item) => item.selected)?.val || + selectSearchYear[0]?.val || + "0"; + const { - labelArray, + labelArray, } = PlanSewageSearch; const { @@ -51,7 +61,6 @@ export default function PlanSewage() { upHeadList3, planSewageList, planSewageCount, - excelList, isLoading, } = PlanSewage; @@ -69,9 +78,9 @@ export default function PlanSewage() { // 그리드 관련 const searchInputRef = useRef<HTMLDivElement & HTMLInputElement>(null); - + // 모달 상태 추가 - + // 초기화 변수 const [resetPage, setResetPage] = useState<boolean>(false); // 표 너비 @@ -85,11 +94,12 @@ export default function PlanSewage() { // 시설상태 const [operationValue, setOperationValue] = useState<string>("0"); // 조회기간 - const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0"); + const [searchYear, setSearchYear] = useState<string>(defaultSearchYear); const [initTrigger, setInitTrigger] = useState<boolean>(false); + // 엑셀다운로드 트리거 + const [isExcelDownClick, setIsExcelDownClick] = useState<boolean>(false); - const [excelTrigger, setExcelTrigger] = useState<boolean>(false); /** * 페이지 함수 */ @@ -100,7 +110,7 @@ export default function PlanSewage() { // 상태값 설정 setFcltCpct(selectUpdownData[0]?.val || "0"); setOperationValue(selectOperationData[0]?.val || "0"); - setSearchYear(selectSearchYear[0]?.val || "0"); + setSearchYear(defaultSearchYear); // SearchInputSelect 초기화 setFcltyName(""); if (searchInputRef.current) { @@ -109,52 +119,39 @@ export default function PlanSewage() { input.value = ""; } } - ref.current.sidoValue='11'; + ref.current.sidoValue = '11'; searchPlanSewage(ref.current, 1, 10); } - useEffect(() => { - if(!excelTrigger) return; - - if (excelList && excelList.length > 0) { - downloadExcel({ - data: excelList as unknown as JSON[], - fileName: '계획하수량 산정지표 현황', - sheetNameArray: ['계획하수량 산정지표 현황'], - headerRange: 'A1:M1' - }); - } - }, [excelList, excelTrigger]); - /** * 콜백함수 */ // 조회 - const searchPlanSewage = useCallback(({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=20) => { + const searchPlanSewage = useCallback(async ({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt: number = 20) => { let nameCd, partStr switch (partValue) { case 'R': if (regionValue !== '0') { - if(unitWaterShedValue==='0') nameCd = regionValue; - else if (middleRegionValue==='0') nameCd = unitWaterShedValue; + if (unitWaterShedValue === '0') nameCd = regionValue; + else if (middleRegionValue === '0') nameCd = unitWaterShedValue; else nameCd = middleRegionValue; } partStr = partValue break; default: - if(sidoValue !== '0') nameCd = ref.current.sidoValue!="0"?ref.current.sidoValue:sidoValue; - if(sigunValue !== '0') nameCd = sigunValue; - partStr = envPartValue !== '0' ? envPartValue:partValue; + if (sidoValue !== '0') nameCd = ref.current.sidoValue != "0" ? ref.current.sidoValue : sidoValue; + if (sigunValue !== '0') nameCd = sigunValue; + partStr = envPartValue !== '0' ? envPartValue : partValue; break; } - if(partStr === 'G'){ - partValue = partValue==='G'?'A':partValue; - searchPlanSewageList(pageNo, pageCnt, searchYear, partValue); + if (partStr === 'G') { + partValue = partValue === 'G' ? 'A' : partValue; + await searchPlanSewageList(pageNo, pageCnt, searchYear, partValue); + } + else { + await searchPlanSewageList(pageNo, pageCnt, searchYear, partStr, nameCd || '0', fcltCpct, operationValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined); } - else{ - searchPlanSewageList(pageNo, pageCnt, searchYear, partStr, nameCd || '0', fcltCpct, operationValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined); - } }, [operationValue, fcltyName, searchPlanSewageList, searchYear, fcltCpct]); /** @@ -162,25 +159,81 @@ export default function PlanSewage() { */ useEffect(() => { if (!isInit) { - if(!initTrigger){ + if (!initTrigger) { initializeCommonData(); searchPlanSewage(ref.current, 1, 10); setInitTrigger(true); } } - }, [isInit, initializeCommonData, searchPlanSewageList, selectSearchYear, initTrigger, searchPlanSewage]); + }, [isInit, initializeCommonData, initTrigger, searchPlanSewage]); - const chkVal = useCallback((returntype:ReturnType) => { + const chkVal = useCallback((returntype: ReturnType) => { // setWidthSize(returntype.partValue=='A' ?"w-[120%]":"w-[130%]") - setUpheadList(returntype.partValue == 'R' ? upHeadList2 : returntype.partValue == 'G'?upHeadList3:upHeadList1); + setUpheadList(returntype.partValue == 'R' ? upHeadList2 : returntype.partValue == 'G' ? upHeadList3 : upHeadList1); setResetPage(true); }, [upHeadList1, upHeadList2, upHeadList3]); // 엑셀다운로드 - const excelDownload = useCallback((searchReturn: ReturnType) => { - searchPlanSewage(searchReturn, 1, 10000); - setExcelTrigger(true); - }, [searchPlanSewage]); + const excelDownload = useCallback(async (searchReturn: ReturnType) => { + if (isExcelDownClick) return; + if (ref.current.partValue === 'A') { // 행정별 + if (!ref.current.sidoValue || ref.current.sidoValue === '0') { + alert('엑셀 다운로드는 시도 조건을 선택한 후 이용 가능합니다.'); + return; + } + } else if (ref.current.partValue === 'R') { // 유역별 + if (!ref.current.regionValue || ref.current.regionValue === '0') { + alert('엑셀 다운로드는 유역 조건을 선택한 후 이용 가능합니다.'); + return; + } + } else if (ref.current.partValue === 'G') { // 환경청별 + if (!ref.current.envPartValue || ref.current.envPartValue === '0') { + alert('엑셀 다운로드는 환경청 조건을 선택한 후 이용 가능합니다.'); + return; + } + } + setIsExcelDownClick(true); + try { + await searchPlanSewage(searchReturn, 1, 10000); + const latestExcelList = store.getState().PlanSewage.excelList || []; + if (latestExcelList.length > 0) { + const toNumber = (value: unknown) => { + if (value === null || value === undefined || value === '') return ''; + const parsed = typeof value === "number" ? value : Number(String(value).replace(/,/g, "")); + return Number.isFinite(parsed) ? parsed : ''; + }; + + const numericKeys = [ + "시설용량(m³/일)", + "여유용량(m³/일)", + "변동부하율(%)", + "분류식화율(%)", + ]; + + const excelData = latestExcelList.map((row) => { + const nextRow = { ...row } as Record<string, unknown>; + numericKeys.forEach((key) => { + if (key in nextRow) { + nextRow[key] = toNumber(nextRow[key]); + } + }); + return nextRow; + }); + + downloadExcelFormat({ + data: excelData as unknown as JSON[], + fileName: '계획하수량 산정지표 현황', + sheetNameArray: ['계획하수량 산정지표 현황'], + headerRange: 'A1:M1', + decimalArray0: [9, 10, 11, 12] + }); + } else { + alert("조회된 결과가 없습니다."); + } + } finally { + setIsExcelDownClick(false); + } + }, [isExcelDownClick, searchPlanSewage, store]); // 페이지 변경 const handlePageChange = useCallback((page: number) => { @@ -190,11 +243,11 @@ export default function PlanSewage() { const mainList = planSewageList || []; const total = planSewageCount || 0; - + return ( <MainDiv> <Title title="계획하수량 산정지표 현황" /> - <SearchBaseForm + <SearchBaseForm leftChildren={<> <SelectBox className="col-span-3" label={labelArray[3] as string} selectArray={selectUpdownData} onSelectValue={(val) => setFcltCpct(val.toString())} value={fcltCpct} selectClass={undefined} /> <SearchInput className={"col-span-3"} label={labelArray[4] as string} textValue={fcltyName} setText={setFcltyName} /> @@ -211,17 +264,18 @@ export default function PlanSewage() { btnClass={btnClass} queryClick={chkVal} excelClick={excelDownload} + excelDisabled={isExcelDownClick} initClick={resetVal} - ref={ref} isAdmin={false} initSidoValue={"11"}/> + ref={ref} isAdmin={false} initSidoValue={"11"} /> <ListDiv> <LoadingOverlay isLoading={isLoading} /> <div className="h-full w-full overflow-x-scroll min-[1200px]:overflow-x-hidden"> - <div className="w-[120%] min-[1200px]:w-[100%]"> - <UiTable cellClick={ undefined } headName={"계획하수량 산정지표 현황"} tableData={mainList} headlist={upheadList} pageSize={10} total={total} resetPageNo={resetPage} onPageChange={handlePageChange} widthFixed={true} /> + <div className="w-[120%] min-[1200px]:w-[100%]"> + <UiTable cellClick={undefined} headName={"계획하수량 산정지표 현황"} tableData={mainList} headlist={upheadList} pageSize={10} total={total} resetPageNo={resetPage} onPageChange={handlePageChange} widthFixed={true} /> </div> <UnderComment divHeight={"5"} commentArray={commentArray} /> </div> </ListDiv> </MainDiv> ); -} \ No newline at end of file +} diff --git a/apps/fo/app/pages/(with-layout)/processArea/page.tsx b/apps/fo/app/pages/(with-layout)/processArea/page.tsx index 91bdb6ab0..bca5a7b91 100644 --- a/apps/fo/app/pages/(with-layout)/processArea/page.tsx +++ b/apps/fo/app/pages/(with-layout)/processArea/page.tsx @@ -5,27 +5,36 @@ */ "use client" -import { useFoStore } from "@/app/store"; -import { ContentsDiv, downloadExcel, MainDiv, SearchBaseForm, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components"; +import { StoreContext, useFoStore } from "@/app/store"; +import { useShallow } from 'zustand/react/shallow'; +import { ContentsDiv, downloadExcelFormat, MainDiv, SearchBaseForm, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components"; import ListDiv from "@common/business_components/ui/list_div"; -import { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useContext, useEffect, useRef, useState } from "react"; import ProcessFacilityInfoModal from "./modal/page"; import LoadingOverlay from "@common/business_components/ui/loadingBar"; export default function ProcessArea() { - const {Common, + const { + Common, ProcessArea, ProcessAreaSearch, initializeCommonData, searchProcessAreaList - } = useFoStore((state) => state) || { - Common: { isInit: false, selectSidoData: [], selectSigunData: [], selectRegionData: [], selectUnitWaterShedData: [], selectMiddleRegionData: [], selectEnvPartData: [], selectSearchYear: [{ val: "0" }], btnClass: "" }, - ProcessArea: { processAreaList: [], processAreaCount: 0, excelList: [], upHeadList1: [] }, - ProcessAreaSearch: { processFacilityLabelArray: [] }, - initializeCommonData: () => {}, - searchProcessAreaList: () => {} - }; - + } = useFoStore( + useShallow((state) => ({ // 다른 무관한 상태가 변경되어도 이 컴포넌트는 불필요하게 다시 렌더링 되지 않음. + Common: state.Common, + ProcessArea: state.ProcessArea, + ProcessAreaSearch: state.ProcessAreaSearch, + initializeCommonData: state.initializeCommonData, + searchProcessAreaList: state.searchProcessAreaList, + })) + ); + const store = useContext(StoreContext); + + if (!store) { + throw new Error("StoreContext is not available. Ensure StoreProvider is mounted."); + } + const { isInit, selectSidoData, @@ -37,7 +46,12 @@ export default function ProcessArea() { selectSearchYear, btnClass, } = Common; - + + const defaultSearchYear = + selectSearchYear.find((item) => item.selected)?.val || + selectSearchYear[0]?.val || + "0"; + const { processFacilityLabelArray, } = ProcessAreaSearch; @@ -46,7 +60,6 @@ export default function ProcessArea() { upHeadList1, processAreaList, processAreaCount, - excelList, isLoading, } = ProcessArea; @@ -54,13 +67,15 @@ export default function ProcessArea() { // 그리드 관련 const searchInputRef = useRef<HTMLDivElement & HTMLInputElement>(null); - // 모달 상태 추가 + // 모달 상태 추가 const [selectedRow, setSelectedRow] = useState<number | null>(null); const [fcltyNm, setFcltyNm] = useState<string>(""); const [fcltyCd, setFcltyCd] = useState<string>(""); - + const [resetPage, setResetPage] = useState<boolean>(false); - const [searchYearValue, setSearchYearValue] = useState<string>(selectSearchYear[0]?.val || "0"); + const [searchYearValue, setSearchYearValue] = useState<string>(defaultSearchYear); + + const [isExcelDownClick, setIsExcelDownClick] = useState<boolean>(false); const ref = useRef<ReturnType>({ partValue: '', @@ -74,8 +89,8 @@ export default function ProcessArea() { sigunText: '', }); - const searchProcessArea = useCallback(({ sidoText, sigunText}: ReturnType, pageNo: number, pageCnt:number=20) => { - searchProcessAreaList(pageNo, pageCnt, sidoText, sigunText, searchYearValue); + const searchProcessArea = useCallback(async ({ sidoText, sigunText }: ReturnType, pageNo: number, pageCnt: number = 20) => { + await searchProcessAreaList(pageNo, pageCnt, sidoText, sigunText, searchYearValue); }, [searchProcessAreaList, searchYearValue]); // 조회 @@ -91,8 +106,8 @@ export default function ProcessArea() { input.value = ""; } } - setSearchYearValue(selectSearchYear[0]?.val || "0"); - searchProcessAreaList(1, 10, '', '', selectSearchYear[0]?.val || "0"); + setSearchYearValue(defaultSearchYear); + searchProcessAreaList(1, 10, '', '', defaultSearchYear); } // 셀 클릭 시 모달 표시 @@ -112,24 +127,49 @@ export default function ProcessArea() { useEffect(() => { if (!isInit) { initializeCommonData(); - searchProcessAreaList(1, 10, '', '', selectSearchYear[0]?.val || "0"); + searchProcessAreaList(1, 10, '', '', defaultSearchYear); } - }, [isInit, initializeCommonData, searchProcessAreaList]); + }, [isInit, initializeCommonData, searchProcessAreaList, defaultSearchYear]); - useEffect(() => { - if (excelList && excelList.length > 0) { - downloadExcel({ - data: excelList as unknown as JSON[], - fileName: '처리구역정보', - sheetNameArray: ['처리구역정보'], - headerRange: 'A1:W1' - }); + const excelDownload = useCallback(async () => { + if (isExcelDownClick) return; + // if (ref.current.partValue === 'A') { // 행정별 + // if (!ref.current.sidoValue || ref.current.sidoValue === '0') { + // alert('엑셀 다운로드는 시도 조건을 선택한 후 이용 가능합니다.'); + // return; + // } + // } else if (ref.current.partValue === 'R') { // 유역별 + // if (!ref.current.regionValue || ref.current.regionValue === '0') { + // alert('엑셀 다운로드는 유역 조건을 선택한 후 이용 가능합니다.'); + // return; + // } + // } else if (ref.current.partValue === 'G') { // 환경청별 + // if (!ref.current.envPartValue || ref.current.envPartValue === '0') { + // alert('엑셀 다운로드는 환경청 조건을 선택한 후 이용 가능합니다.'); + // return; + // } + // } + + setIsExcelDownClick(true); + try { + await searchProcessArea(ref.current, 1, 10000); + const latestExcelList = store.getState().ProcessArea.excelList || []; + if (latestExcelList.length > 0) { + downloadExcelFormat({ + data: latestExcelList as unknown as JSON[], + fileName: '처리구역정보', + sheetNameArray: ['처리구역정보'], + headerRange: 'A1:W1', + decimalArray0: [3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15], + decimalArray2: [16, 17, 18, 19, 20, 21], + }); + } else { + alert('조회된 결과가 없습니다.'); + } + } finally { + setIsExcelDownClick(false); } - }, [excelList]); - - const excelDownload = useCallback(() => { - searchProcessArea(ref.current, 1, 10000); - }, [searchProcessArea]); + }, [isExcelDownClick, searchProcessArea, store]); const handlePageChange = useCallback((pageNo: number) => { setResetPage(false); @@ -143,7 +183,7 @@ export default function ProcessArea() { <ContentsDiv> <SearchBaseForm leftChildren={<> - <SelectBox className="col-span-3" label={processFacilityLabelArray[6] as string} selectArray={selectSearchYear} onSelectValue={(val) => setSearchYearValue(val.toString())} value={searchYearValue} selectClass={undefined}/> + <SelectBox className="col-span-3" label={processFacilityLabelArray[6] as string} selectArray={selectSearchYear} onSelectValue={(val) => setSearchYearValue(val.toString())} value={searchYearValue} selectClass={undefined} /> </>} rightChildren={undefined} selectSidoData={selectSidoData} @@ -155,6 +195,7 @@ export default function ProcessArea() { btnClass={btnClass} queryClick={chkVal} excelClick={excelDownload} + excelDisabled={isExcelDownClick} initClick={resetVal} ref={ref} isAdmin={true} @@ -163,19 +204,20 @@ export default function ProcessArea() { <LoadingOverlay isLoading={isLoading} /> <div className="h-full w-full overflow-x-scroll min-[1900px]:overflow-x-hidden"> <div className="w-[160%] min-[1900px]:w-[100%]"> - <UiTable cellClick={(index: number | undefined) => index !== undefined && cellClick(index)} headName={"처리구역정보"} tableData={mainList} headlist={upHeadList1} pageSize={10} total={total} resetPageNo={resetPage} onPageChange={handlePageChange} widthFixed={true} /> + <UiTable cellClick={(index: number | undefined) => index !== undefined && cellClick(index)} headName={"처리구역정보"} tableData={mainList} + headlist={upHeadList1} pageSize={10} total={total} resetPageNo={resetPage} onPageChange={handlePageChange} widthFixed={true} /> </div> </div> </ListDiv> </ContentsDiv> {selectedRow !== null && ( - <UiModal - body={<ProcessFacilityInfoModal fcltyName={fcltyNm} facilityCd={fcltyCd}></ProcessFacilityInfoModal>} - bodyClass="sm:max-w-[90vw] w-[1200px] h-[900px]" + <UiModal + body={<ProcessFacilityInfoModal fcltyName={fcltyNm} facilityCd={fcltyCd}></ProcessFacilityInfoModal>} + bodyClass="sm:max-w-[90vw] w-[1200px] h-[100dvh] max-h-[900px] overflow-y-auto" open={selectedRow !== null} onOpenChange={(open: boolean) => !open && closeModal()} /> )} </MainDiv> ); -} \ No newline at end of file +} diff --git a/apps/fo/app/pages/(with-layout)/processFacility/modal/page.tsx b/apps/fo/app/pages/(with-layout)/processFacility/modal/page.tsx index 7e3f7c52f..3118e008f 100644 --- a/apps/fo/app/pages/(with-layout)/processFacility/modal/page.tsx +++ b/apps/fo/app/pages/(with-layout)/processFacility/modal/page.tsx @@ -5,6 +5,7 @@ */ "use client" +import { useCallback, useRef } from "react"; import { useFoStore } from "@/app/store"; import type { TableUpperProps } from "@/app/store/dashboard"; import { downloadExcelFormat, filePickedBlobArray, UITab, UiTable } from "@common/business_components/ui"; @@ -121,6 +122,21 @@ export default function ProcessFacilityInfoModal(props: ModalType) { const upHeadListPlanInfoChange = headMakeList(upHeadListPlanInfo, 2); const upHeadListFcltyHistInfoChange = headMakeList(upHeadListFcltyHistInfo, 3); const upHeadListFcltyWaterQltyInfoChange = headMakeList(upHeadListFcltyWaterQltyInfo, 4); + const historyTopScrollRef = useRef<HTMLDivElement>(null); + const historyBottomScrollRef = useRef<HTMLDivElement>(null); + const syncLockRef = useRef(false); + + const syncScroll = useCallback((source: HTMLDivElement | null, target: HTMLDivElement | null) => { + if (!source || !target || syncLockRef.current) return; + syncLockRef.current = true; + const nextTop = source.scrollTop; + if (target.scrollTop !== nextTop) { + target.scrollTop = nextTop; + } + requestAnimationFrame(() => { + syncLockRef.current = false; + }); + }, []); const excelDownloadPlan = () => { const excelDownList: any[] = []; @@ -137,11 +153,19 @@ export default function ProcessFacilityInfoModal(props: ModalType) { "시운전완료일": item.trlrnCmplDt, "가동개시일": item.rnDt, "시설용량(m³/일)": item.fctCpc&&item.fctCpc.toString().trim()!=''?Number(item.fctCpc.toString().replaceAll(',','')):'', + "공법구분": item.trtSrsHanNm, + "공법계열": item.trtMocHanNm, + "방법": item.trtFrfHanNm, }); }); } if (excelDownList && excelDownList.length > 0) { - downloadExcelFormat({ data: JSON.parse(JSON.stringify(excelDownList)), fileName: props.fcltyName + ' 시설이력관리', sheetNameArray: [props.fcltyName + ' 시설이력관리'], headerRange: 'A1:I1' }); + downloadExcelFormat({ + data: JSON.parse(JSON.stringify(excelDownList)), + fileName: props.fcltyName + ' 시설이력관리', + sheetNameArray: [props.fcltyName + ' 시설이력관리'], + decimalArray0: [8], + headerRange: 'A1:L1' }); } else if (gridListFcltyHistInfo.length == 0) { alert('다운받을 데이터가 없습니다.'); } @@ -182,7 +206,7 @@ export default function ProcessFacilityInfoModal(props: ModalType) { const sheetParameter = [ { data: excelData, - headerNum: 2, + headerNum: 3, headerRange: 'A1:S2', sheetName: '시설 수질정보', startCol: 'A' @@ -218,7 +242,7 @@ export default function ProcessFacilityInfoModal(props: ModalType) { <h3 className="text-2xl font-medium text-label">시설 기본정보</h3> </div> <div className="card !m-[1.2rem] max-h-[40rem] overflow-auto !p-0r"> - <Table> + <Table variant="secondary"> <thead> <TableRow className=""> <TableHead className="w-1/6">{'시설명'}</TableHead> @@ -262,7 +286,11 @@ export default function ProcessFacilityInfoModal(props: ModalType) { <TabsContent value="history_info"> <ListDiv> - <div className="h-[250px] w-full overflow-y-auto overflow-x-hidden"> + <div + className="h-[250px] w-full overflow-y-auto overflow-x-hidden" + ref={historyTopScrollRef} + onScroll={() => syncScroll(historyTopScrollRef.current, historyBottomScrollRef.current)} + > <Button className={btnClass} style={{float: 'right'}} color="green" onClick={excelDownloadPlan}> {"엑셀다운로드"} </Button> @@ -274,7 +302,11 @@ export default function ProcessFacilityInfoModal(props: ModalType) { </div> </ListDiv> <ListDiv> - <div className="h-[250px] w-full overflow-y-auto overflow-x-hidden"> + <div + className="h-[250px] w-full overflow-y-auto overflow-x-hidden" + ref={historyBottomScrollRef} + onScroll={() => syncScroll(historyBottomScrollRef.current, historyTopScrollRef.current)} + > <Button className={btnClass} style={{float: 'right'}} color="green" onClick={excelDownloadQlty}> {"엑셀다운로드"} </Button> @@ -292,4 +324,4 @@ export default function ProcessFacilityInfoModal(props: ModalType) { </div> </> ); -} \ No newline at end of file +} diff --git a/apps/fo/app/pages/(with-layout)/processFacility/page.tsx b/apps/fo/app/pages/(with-layout)/processFacility/page.tsx index 856ab4631..def1ca0e8 100644 --- a/apps/fo/app/pages/(with-layout)/processFacility/page.tsx +++ b/apps/fo/app/pages/(with-layout)/processFacility/page.tsx @@ -6,11 +6,11 @@ "use client" import UnderComment from "@/app/components/common/UnderComment"; -import { useFoStore } from "@/app/store"; -import { ContentsDiv, downloadExcel, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components"; +import { StoreContext, useFoStore } from "@/app/store"; +import { ContentsDiv, downloadExcelFormat, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components"; import ListDiv from "@common/business_components/ui/list_div"; import LoadingOverlay from "@common/business_components/ui/loadingBar"; -import { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useContext, useEffect, useRef, useState } from "react"; import ProcessFacilityInfoModal from "./modal/page"; export default function ProcessFacilitySearch() { @@ -25,6 +25,11 @@ export default function ProcessFacilitySearch() { //시설데이터 수질정보 조회 ,searchProcessFacilityWaterQlty } = useFoStore((state) => state) || { ProcessFacilityList: { processFacilityList: [], processFacilityCount: 0 }}; + const store = useContext(StoreContext); + + if (!store) { + throw new Error("StoreContext is not available. Ensure StoreProvider is mounted."); + } const { isInit, @@ -69,7 +74,6 @@ export default function ProcessFacilitySearch() { upHeadList1, upHeadList2, upHeadList3, - excelList, isLoading, } = ProcessFacilityList; @@ -89,6 +93,7 @@ export default function ProcessFacilitySearch() { const [partValue, setPartValue] = useState<string>("A"); // const [widthSize, setWidthSize] = useState<string>("w-[120%]"); const [upHeadList, setUpheadList] = useState(upHeadList1); + const [isExcelDownClick, setIsExcelDownClick] = useState<boolean>(false); /** * 모달 상태 변수 @@ -149,13 +154,7 @@ export default function ProcessFacilitySearch() { } }, [partValue, upHeadList1, upHeadList2, upHeadList3]); - useEffect(() => { - if (excelList && excelList.length > 0) { - downloadExcel({ data: excelList as unknown as JSON[], fileName: '처리시설 시설정보', sheetNameArray: ['처리시설 시설정보'], headerRange: 'A1:P1' }); - } - }, [excelList]); - - const searchFcltyList = useCallback(({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=10) => { + const searchFcltyList = useCallback(async ({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=10) => { let nameCd, partStr switch (partValue) { case 'R': @@ -172,7 +171,7 @@ export default function ProcessFacilitySearch() { partStr = envPartValue !== '0' ? envPartValue:partValue; break; } - searchProcessFacility(pageNo, pageCnt, partStr, nameCd || '0', fcltCpct, operationValue, facilityPartValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined); + await searchProcessFacility(pageNo, pageCnt, partStr, nameCd || '0', fcltCpct, operationValue, facilityPartValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined); if(isSearch) { setIsSearch(false); } @@ -235,9 +234,44 @@ export default function ProcessFacilitySearch() { } } - const excelDownload = useCallback(() => { - searchFcltyList(ref.current, 1, 10000); - }, [searchFcltyList]); + const excelDownload = useCallback(async () => { + if (isExcelDownClick) return; + // if (ref.current.partValue === 'A') { // 행정별 + // if (!ref.current.sidoValue || ref.current.sidoValue === '0') { + // alert('엑셀 다운로드는 시도 조건을 선택한 후 이용 가능합니다.'); + // return; + // } + // } else if (ref.current.partValue === 'R') { // 유역별 + // if (!ref.current.regionValue || ref.current.regionValue === '0') { + // alert('엑셀 다운로드는 유역 조건을 선택한 후 이용 가능합니다.'); + // return; + // } + // } else if (ref.current.partValue === 'G') { // 환경청별 + // if (!ref.current.envPartValue || ref.current.envPartValue === '0') { + // alert('엑셀 다운로드는 환경청 조건을 선택한 후 이용 가능합니다.'); + // return; + // } + // } + + setIsExcelDownClick(true); + try { + await searchFcltyList(ref.current, 1, 10000); + const latestExcelList = store.getState().ProcessFacilityList.excelList || []; + if (latestExcelList.length > 0) { + downloadExcelFormat({ + data: latestExcelList as unknown as JSON[], + fileName: '처리시설 시설정보', + sheetNameArray: ['처리시설 시설정보'], + headerRange: 'A1:P1', + decimalArray0: [9, 10, 11], + }); + } else { + alert('조회된 결과가 없습니다.'); + } + } finally { + setIsExcelDownClick(false); + } + }, [isExcelDownClick, searchFcltyList, store]); // 모달 닫기 const closeModal = () => { @@ -266,6 +300,7 @@ export default function ProcessFacilitySearch() { btnClass={btnClass} queryClick={chkVal} excelClick={excelDownload} + excelDisabled={isExcelDownClick} initClick={resetVal} ref={ref} isAdmin={false} /> <ListDiv> @@ -292,11 +327,11 @@ export default function ProcessFacilitySearch() { {selectedRow !== null && ( <UiModal body={<ProcessFacilityInfoModal fcltyName={modalFcltyNm} facilityCd={modalFcltyCd} sido={modalSido} gugun={modalGugun} daddr={modalAddr} fctCpc={modalFctCpc} trtFrfNm={modalTrtFrfNm} rnDt={modalRnDt} cmplDt={modalCmplDt} trstOprEtpNm={modalTrstOprEtpNm} bscFctClsNm={modalBscFctClsNm} mnstr={modalMnstr} efflWtrrgn={modalEfflWtrrgn} lgbsn={modalLgbsn} efflWqltStndrdNm={modalEfflWqltStndrdNm} rgnNm={modalRgnNm} rvbsn={modalRvbsn}></ProcessFacilityInfoModal>} - bodyClass="sm:max-w-[70vw] sm:max-h-[95vh]" + bodyClass="sm:max-w-[100vw] w-[1300px] h-[100dvh] max-h-[800px]" open={selectedRow !== null} onOpenChange={(open) => !open && closeModal()} /> )} </MainDiv> ); -} \ No newline at end of file +} diff --git a/apps/fo/app/pages/(with-layout)/rdi/page.tsx b/apps/fo/app/pages/(with-layout)/rdi/page.tsx index 47142e0a1..ff4b8d205 100644 --- a/apps/fo/app/pages/(with-layout)/rdi/page.tsx +++ b/apps/fo/app/pages/(with-layout)/rdi/page.tsx @@ -5,11 +5,11 @@ */ "use client" -import { useFoStore } from "@/app/store"; +import { StoreContext, useFoStore } from "@/app/store"; import type { TableUpperProps } from "@/app/store/common"; -import { ContentsDiv, downloadExcel, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiTable, type ReturnType } from "@common/business_components"; +import { ContentsDiv, downloadExcelFormat, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiTable, type ReturnType } from "@common/business_components"; import ListDiv from "@common/business_components/ui/list_div"; -import { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useContext, useEffect, useRef, useState } from "react"; import LoadingOverlay from "@common/business_components/ui/loadingBar"; import UnderComment from "@/app/components/common/UnderComment"; @@ -26,6 +26,11 @@ export default function Rdi() { ,RdiSearch ,searchRdiList } = useFoStore((state) => state) || { processAreaList: { processAreaList: [] }}; + const store = useContext(StoreContext); + + if (!store) { + throw new Error("StoreContext is not available. Ensure StoreProvider is mounted."); + } const { isInit, @@ -41,6 +46,11 @@ export default function Rdi() { btnClass } = Common; + const defaultSearchYear = + selectSearchYear.find((item) => item.selected)?.val || + selectSearchYear[0]?.val || + "0"; + const { labelArray, } = RdiSearch; @@ -51,7 +61,6 @@ export default function Rdi() { upHeadList3, rdiList, rdiCount, - excelList, isLoading, } = Rdi; @@ -83,9 +92,10 @@ export default function Rdi() { // 시설상태 const [operationValue, setOperationValue] = useState<string>("0"); // 조회기간 - const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0"); + const [searchYear, setSearchYear] = useState<string>(defaultSearchYear); // 엑셀 다운로드 트리거 - const [isInitExcelDownload, setIsInitExcelDownload] = useState<boolean>(false); + const [isExcelDownClick, setIsExcelDownClick] = useState<boolean>(false); + const [initTrigger, setInitTrigger] = useState<boolean>(false); /** @@ -99,7 +109,7 @@ export default function Rdi() { // 상태값 설정 setFcltCpct(selectUpdownData[0]?.val || "0"); setOperationValue(selectOperationData[0]?.val || "0"); - setSearchYear(selectSearchYear[0]?.val || "0"); + setSearchYear(defaultSearchYear); // SearchInputSelect 초기화 setFcltyName(""); if (searchInputRef.current) { @@ -112,23 +122,11 @@ export default function Rdi() { searchRdi(ref.current, 1, 10); } - useEffect(() => { - if(!isInitExcelDownload) return ; - if (excelList && excelList.length > 0) { - downloadExcel({ - data: excelList as unknown as JSON[], - fileName: 'RDII추정', - sheetNameArray: ['RDII추정'], - headerRange: 'A1:R1' - }); - } - }, [excelList, isInitExcelDownload]); - /** * 콜백함수 */ // 조회 - const searchRdi = useCallback(({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=20) => { + const searchRdi = useCallback(async ({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=20) => { let nameCd, partStr switch (partValue) { case 'R': @@ -147,10 +145,9 @@ export default function Rdi() { } if(partStr === 'G'){ - searchRdiList(pageNo, pageCnt, searchYear); + await searchRdiList(pageNo, pageCnt, searchYear); }else{ - searchRdiList(pageNo, pageCnt, searchYear, partStr, nameCd || '0', fcltCpct, operationValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined); - + await searchRdiList(pageNo, pageCnt, searchYear, partStr, nameCd || '0', fcltCpct, operationValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined); } }, [operationValue, fcltyName, searchRdiList, searchYear, fcltCpct]); @@ -174,10 +171,71 @@ export default function Rdi() { }, [upHeadList1, upHeadList2, upHeadList3]); // 엑셀다운로드 - const excelDownload = useCallback(() => { - searchRdi(ref.current, 1, 5000); - setIsInitExcelDownload(true); - }, [searchRdi]); + const excelDownload = useCallback(async () => { + if (isExcelDownClick) return; + // if (ref.current.partValue === 'A') { // 행정별 + // if (!ref.current.sidoValue || ref.current.sidoValue === '0') { + // alert('엑셀 다운로드는 시도 조건을 선택한 후 이용 가능합니다.'); + // return; + // } + // } else if (ref.current.partValue === 'R') { // 유역별 + // if (!ref.current.regionValue || ref.current.regionValue === '0') { + // alert('엑셀 다운로드는 유역 조건을 선택한 후 이용 가능합니다.'); + // return; + // } + // } else if (ref.current.partValue === 'G') { // 환경청별 + // if (!ref.current.envPartValue || ref.current.envPartValue === '0') { + // alert('엑셀 다운로드는 환경청 조건을 선택한 후 이용 가능합니다.'); + // return; + // } + // } + + setIsExcelDownClick(true); + try { + await searchRdi(ref.current, 1, 5000); + const latestExcelList = store.getState().Rdi.excelList || []; + if (latestExcelList.length > 0) { + const toNumber = (value: unknown) => { + if (value === null || value === undefined || value === '') return ''; + const parsed = typeof value === "number" ? value : Number(String(value).replace(/,/g, "")); + return Number.isFinite(parsed) ? parsed : ''; + }; + + const numericKeys = [ + "시설용량(m³/일)", + "청천시유입하수량(m³/일)", + "하절기강우일최저유입BOD(mg/L)", + "하절기최저유입BOD유입유량(m³/일)", + "동절기청천시유입BOD(mg/L)", + "RDII추정", + "RDII추정시일강수량(mm)", + "강우시총추정유입량(m³/일)", + ]; + + const excelData = latestExcelList.map((row) => { + const nextRow = { ...row } as Record<string, unknown>; + numericKeys.forEach((key) => { + if (key in nextRow) { + nextRow[key] = toNumber(nextRow[key]); + } + }); + return nextRow; + }); + + downloadExcelFormat({ + data: excelData as unknown as JSON[], + fileName: 'RDII추정', + sheetNameArray: ['RDII추정'], + headerRange: 'A1:R1', + decimalArray0: [9, 10, 11, 12, 13, 14, 15, 16], + }); + } else { + alert("조회된 결과가 없습니다."); + } + } finally { + setIsExcelDownClick(false); + } + }, [isExcelDownClick, searchRdi, store]); // 페이지 변경 const handlePageChange = useCallback((page: number) => { @@ -209,13 +267,15 @@ export default function Rdi() { btnClass={btnClass} queryClick={chkVal} excelClick={excelDownload} + excelDisabled={isExcelDownClick} initClick={resetVal} ref={ref} isAdmin={false} initSidoValue={"11"}/> <ListDiv> <LoadingOverlay isLoading={isLoading} /> <div className="h-full w-full overflow-x-scroll min-[1200px]:overflow-x-hidden"> <div className="w-[120%] min-[1200px]:w-[100%]"> - <UiTable cellClick={ undefined } headName={"RDII추정"} tableData={mainList} headlist={upheadList} pageSize={10} total={total} resetPageNo={resetPage} onPageChange={handlePageChange} widthFixed={true} /> + <UiTable cellClick={ undefined } headName={"RDII추정"} tableData={mainList} headlist={upheadList} + pageSize={10} total={total} resetPageNo={resetPage} onPageChange={handlePageChange} widthFixed={true} /> </div> <UnderComment divHeight={"0"} commentArray={commentArray} /> </div> @@ -223,4 +283,4 @@ export default function Rdi() { </ContentsDiv> </MainDiv> ); -} \ No newline at end of file +} diff --git a/apps/fo/app/pages/(with-layout)/sewageOccur/modal/page.tsx b/apps/fo/app/pages/(with-layout)/sewageOccur/modal/page.tsx index bc7fec4e8..223dc055a 100644 --- a/apps/fo/app/pages/(with-layout)/sewageOccur/modal/page.tsx +++ b/apps/fo/app/pages/(with-layout)/sewageOccur/modal/page.tsx @@ -8,7 +8,7 @@ import { roundNumber } from "@/app/components/common/DataForm"; import { useFoStore } from "@/app/store"; import type { modalTableType } from "@/app/store/sewageOccur"; -import { DateMonthPicker, downloadExcel, UiTable } from "@common/business_components/ui"; +import { DateMonthPicker, downloadExcelHeaders, UiTable } from "@common/business_components/ui"; import ListDiv from "@common/business_components/ui/list_div"; import { Button, Table, TableCell, TableHead, TableRow } from "@common/components/ui"; import { useEffect, useState } from "react"; @@ -69,20 +69,25 @@ export default function SewageOccurModal(props: { fcltyName: string; facilityCd } }, [endYearMonth, startYearMonth]); - const excelDownload = () => { + const excelDownload = () => { + // 헤더 순서를 명시적으로 정의 + const headers = ["날짜", "물사용량(m³/일)", "오수전환율(%)", "오수발생량 추정(m³/일)"] as const; + const excelDownList: any[] = []; if(detailList.length > 0){ detailList.forEach((item) => { - excelDownList.push({ - "날짜": item.msrDt, - "물사용량(m³/일)": roundNumber(item.waterUsage, 0), - "오수전환율(%)": roundNumber(item.swageChangePercent, 0), - "오수발생량 추정(m³/일)": roundNumber(item.estimationSewage, 0), - }); + // 순서를 보장하기 위해 헤더 순서대로 객체 생성 + const row: Record<string, string | number> = {}; + row[headers[0]] = item.msrDt; + row[headers[1]] = roundNumber(item.waterUsage, 0); + row[headers[2]] = roundNumber(item.swageChangePercent, 0); + row[headers[3]] = roundNumber(item.estimationSewage, 0); + excelDownList.push(row); }); } if (excelDownList && excelDownList.length > 0) { - downloadExcel({ data: JSON.parse(JSON.stringify(excelDownList)), fileName: props.searchYear+'년 '+props.fcltyName + ' 오수발생량 추정', sheetNameArray: [props.fcltyName + ' 오수발생량 추정'], headerRange: 'A1:D1' }); + downloadExcelHeaders({ data: JSON.parse(JSON.stringify(excelDownList)), fileName: props.searchYear+'년 '+props.fcltyName + ' 오수발생량 추정' + , sheetNameArray: [props.fcltyName + ' 오수발생량 추정'], headerRange: 'A1:D1', headers }); } else if (detailList.length == 0) { alert('다운받을 데이터가 없습니다.'); } diff --git a/apps/fo/app/pages/(with-layout)/sewageOccur/page.tsx b/apps/fo/app/pages/(with-layout)/sewageOccur/page.tsx index 001e816ae..95aa8961a 100644 --- a/apps/fo/app/pages/(with-layout)/sewageOccur/page.tsx +++ b/apps/fo/app/pages/(with-layout)/sewageOccur/page.tsx @@ -6,13 +6,13 @@ */ "use client" -import { useFoStore } from "@/app/store"; +import { StoreContext, useFoStore } from "@/app/store"; import { type TableUpperProps } from "@/app/store/common"; import type { SewageOccurProps } from "@/app/store/sewageOccur"; -import { ContentsDiv, downloadExcel, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components"; +import { ContentsDiv, downloadExcelFormat, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components"; import ListDiv from "@common/business_components/ui/list_div"; import LoadingOverlay from "@common/business_components/ui/loadingBar"; -import { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useContext, useEffect, useRef, useState } from "react"; import SewageOccurModal from "./modal/page"; export default function PlanSewage() { @@ -29,6 +29,11 @@ export default function PlanSewage() { searchSewageOccurList: () => {}, calculateModalDataForAllFacilities: async () => [] }; + const store = useContext(StoreContext); + + if (!store) { + throw new Error("StoreContext is not available. Ensure StoreProvider is mounted."); + } const { isInit, @@ -44,6 +49,11 @@ export default function PlanSewage() { btnClass } = Common; + const defaultSearchYear = + selectSearchYear.find((item) => item.selected)?.val || + selectSearchYear[0]?.val || + "0"; + const { labelArray, } = SewageOccurSearch; @@ -54,7 +64,6 @@ export default function PlanSewage() { upHeadList3, sewageOccurList, sewageOccurCount, - excelList, isLoading, } = SewageOccur; @@ -93,10 +102,9 @@ export default function PlanSewage() { // 시설상태 const [operationValue, setOperationValue] = useState<string>("0"); // 조회기간 - const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0"); - // 엑셀다운로드 트리거 - const [isInitExcelDownload, setIsInitExcelDownload] = useState<boolean>(false); - + const [searchYear, setSearchYear] = useState<string>(defaultSearchYear); + // 엑셀 조회 중 상태 + const [isExcelDownClick, setIsExcelDownClick] = useState<boolean>(false); /** * 페이지 함수 */ @@ -107,7 +115,7 @@ export default function PlanSewage() { // 상태값 설정 setFcltCpct(selectUpdownData[0]?.val || "0"); setOperationValue(selectOperationData[0]?.val || "0"); - setSearchYear(selectSearchYear[0]?.val || "0"); + setSearchYear(defaultSearchYear); // SearchInputSelect 초기화 setFcltyName(""); if (searchInputRef.current) { @@ -116,7 +124,7 @@ export default function PlanSewage() { input.value = ""; } } - searchSewageOccurList(1, 10, selectSearchYear[0]?.val || "0"); + searchSewageOccurList(1, 10, defaultSearchYear); } // 모달 닫기 @@ -139,32 +147,15 @@ export default function PlanSewage() { useEffect(() => { if (!isInit) { initializeCommonData(); - searchSewageOccurList(1, 10, selectSearchYear[0]?.val || "0"); + searchSewageOccurList(1, 10, defaultSearchYear); } - }, [isInit, initializeCommonData, searchSewageOccurList, selectSearchYear]); - - useEffect(() => { - if(!isInitExcelDownload) return ; - - const timer = setTimeout(() => { - if (excelList && excelList.length > 0) { - downloadExcel({ - data: excelList as unknown as JSON[], - fileName: '오수발생량 추정', - sheetNameArray: ['오수발생량 추정'], - headerRange: 'A1:O1' - }); - } - }, 500); // 약간의 지연을 두고 다운로드 실행 - - return () => clearTimeout(timer); - }, [excelList, isInit, isInitExcelDownload]); + }, [isInit, initializeCommonData, searchSewageOccurList, defaultSearchYear]); /** * 콜백함수 */ // 조회 - const searchFacilityPrecipitation = useCallback(({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=20) => { + const searchFacilityPrecipitation = useCallback(async ({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=20) => { let nameCd, partStr switch (partValue) { case 'R': @@ -182,9 +173,10 @@ export default function PlanSewage() { break; } if (partStr.length > 1 || fcltCpct !== '0' || operationValue!== '0' ||nameCd ||(fcltyName.trim() !== "" && fcltyName.trim() !== null)) { - searchSewageOccurList(pageNo, pageCnt, searchYear, partStr, nameCd || '0', fcltCpct, operationValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined); + await searchSewageOccurList(pageNo, pageCnt, searchYear, partStr, nameCd || '0', fcltCpct, operationValue + , fcltyName.trim() !== ""? fcltyName : undefined); } else { - searchSewageOccurList(pageNo, pageCnt, searchYear); + await searchSewageOccurList(pageNo, pageCnt, searchYear); } }, [fcltCpct, operationValue, fcltyName, searchSewageOccurList, searchYear]); @@ -195,10 +187,68 @@ export default function PlanSewage() { }, [upHeadList1, upHeadList2, upHeadList3]); // 엑셀다운로드 - const excelDownload = useCallback(() => { - searchFacilityPrecipitation(ref.current, 1, 5000); - setIsInitExcelDownload(true); - }, [searchFacilityPrecipitation]); + const excelDownload = useCallback(async () => { + if (isExcelDownClick) return; + if (ref.current.partValue === 'A') { // + if (!ref.current.sidoValue || ref.current.sidoValue === '0') { + alert('엑셀 다운로드는 시도 조건을 선택한 후 이용 가능합니다.'); + return; + } + } else if (ref.current.partValue === 'R') { // 유역별 + if (!ref.current.regionValue || ref.current.regionValue === '0') { + alert('엑셀 다운로드는 유역 조건을 선택한 후 이용 가능합니다.'); + return; + } + } else if (ref.current.partValue === 'G') { // 환경청별 + if (!ref.current.envPartValue || ref.current.envPartValue === '0') { + alert('엑셀 다운로드는 환경청 조건을 선택한 후 이용 가능합니다.'); + return; + } + } + setIsExcelDownClick(true); + try { + await searchFacilityPrecipitation(ref.current, 1, 5000); + const latestExcelList = store.getState().SewageOccur.excelList || []; + if (latestExcelList.length > 0) { + const toNumber = (value: unknown) => { + if (value === null || value === undefined || value === '') return ''; + const parsed = typeof value === "number" ? value : Number(String(value).replace(/,/g, "")); + return Number.isFinite(parsed) ? parsed : ''; + }; + + const numericKeys = [ + "시설용량(m³/일)", + "물사용량(m³/일)", + "오수전환율(%)", + "오수발생량추정(m³/일)", + "연간하수유입량(m³/일)", + "청천시하수유입량(m³/일)", + ]; + + const excelData = latestExcelList.map((row) => { + const nextRow = { ...row } as Record<string, unknown>; + numericKeys.forEach((key) => { + if (key in nextRow) { + nextRow[key] = toNumber(nextRow[key]); + } + }); + return nextRow; + }); + + downloadExcelFormat({ + data: excelData as unknown as JSON[], + fileName: '오수발생량 추정', + sheetNameArray: ['오수발생량 추정'], + headerRange: 'A1:O1', + decimalArray0: [9, 10, 11, 12, 13, 14] + }); + } else { + alert("조회된 결과가 없습니다."); + } + } finally { + setIsExcelDownClick(false); + } + }, [isExcelDownClick, searchFacilityPrecipitation]); // 페이지 변경 const handlePageChange = useCallback((page: number) => { @@ -230,6 +280,7 @@ export default function PlanSewage() { btnClass={btnClass} queryClick={chkVal} excelClick={excelDownload} + excelDisabled={isExcelDownClick} initClick={resetVal} ref={ref} isAdmin={false} @@ -250,7 +301,7 @@ export default function PlanSewage() { facilityCd={fcltyCd} searchYear={searchYear} />} - bodyClass="sm:max-w-[90vw] w-[1000px] h-[770px]" + bodyClass="sm:max-w-[90vw] w-[1000px] h-[100dvh] max-h-[770px]" open={selectedRow !== null} onOpenChange={(open) => !open && closeModal()} noHeaderYn="Y" diff --git a/apps/fo/app/pages/(with-layout)/underWaterInput/modal/page.tsx b/apps/fo/app/pages/(with-layout)/underWaterInput/modal/page.tsx index 2e0f5f27e..02b1891ad 100644 --- a/apps/fo/app/pages/(with-layout)/underWaterInput/modal/page.tsx +++ b/apps/fo/app/pages/(with-layout)/underWaterInput/modal/page.tsx @@ -29,7 +29,7 @@ const tabList1 = [ export default function UnderWaterInputModal({fcltyCd='', fcltyName='',searchYear=new Date().getFullYear().toString()} : ModalProps) { // 그리드 관련 - const {Common, UnderWaterDetail, unserWaterModal} = useFoStore((state) => state); + const {Common, UnderWaterDetail, underWaterModal} = useFoStore((state) => state); const { btnClass } = Common; const [startYmDate, setStartYmDate] = useState<string>(searchYear+'01'); const [endYmDate, setEndYmDate] = useState<string>(searchYear+'12'); @@ -74,8 +74,8 @@ export default function UnderWaterInputModal({fcltyCd='', fcltyName='',searchYea }, [searchYear, tabTerm]); useEffect(()=>{ - unserWaterModal(fcltyCd, searchYear, searchYear+'01', searchYear+'12'); - }, [fcltyCd, searchYear, unserWaterModal]); + underWaterModal(fcltyCd, searchYear, searchYear+'01', searchYear+'12'); + }, [fcltyCd, searchYear, underWaterModal]); useEffect(() => { if(endYmDate < startYmDate) { @@ -93,8 +93,8 @@ export default function UnderWaterInputModal({fcltyCd='', fcltyName='',searchYea }, [startYmDate, endYmDate, gridListUnderWaterDetail, prevStartYmDate, prevEndYmDate]); useEffect(() => { - unserWaterModal(fcltyCd, searchYear, prevStartYmDate, prevEndYmDate); - } , [fcltyCd, searchYear, unserWaterModal, prevStartYmDate, prevEndYmDate]); + underWaterModal(fcltyCd, searchYear, prevStartYmDate, prevEndYmDate); + } , [fcltyCd, searchYear, underWaterModal, prevStartYmDate, prevEndYmDate]); const excelDownload = () => { const excelDownList: any[] = []; diff --git a/apps/fo/app/pages/(with-layout)/underWaterInput/page.tsx b/apps/fo/app/pages/(with-layout)/underWaterInput/page.tsx index 47e9ec912..36fbff308 100644 --- a/apps/fo/app/pages/(with-layout)/underWaterInput/page.tsx +++ b/apps/fo/app/pages/(with-layout)/underWaterInput/page.tsx @@ -7,11 +7,11 @@ */ "use client" -import { useFoStore } from "@/app/store"; +import { StoreContext, useFoStore } from "@/app/store"; import type { TableUpperProps } from "@/app/store/common"; -import { ContentsDiv, downloadExcel, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components"; +import { ContentsDiv, downloadExcelFormat, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components"; import ListDiv from "@common/business_components/ui/list_div"; -import { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useContext, useEffect, useRef, useState } from "react"; import UnderWaterInputModal from "./modal/page"; import type { UnderWaterProps } from "@/app/store/underWaterInput/types"; import LoadingOverlay from "@common/business_components/ui/loadingBar"; @@ -35,6 +35,11 @@ export default function UnderWaterInput() { initializeCommonData: () => {}, searchUnderWaterList: () => {} }; + const store = useContext(StoreContext); + + if (!store) { + throw new Error("StoreContext is not available. Ensure StoreProvider is mounted."); + } const { isInit, @@ -50,6 +55,11 @@ export default function UnderWaterInput() { btnClass } = Common; + const defaultSearchYear = + selectSearchYear.find((item) => item.selected)?.val || + selectSearchYear[0]?.val || + "0"; + const { labelArray, } = UnderWaterSearch; @@ -60,7 +70,6 @@ export default function UnderWaterInput() { upHeadList3, underWaterList, underWaterCount, - excelList, isLoading, } = UnderWater; @@ -98,9 +107,9 @@ export default function UnderWaterInput() { // 시설상태 const [operationValue, setOperationValue] = useState<string>("0"); // 조회기간 - const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0"); + const [searchYear, setSearchYear] = useState<string>(defaultSearchYear); // 엑셀다운로드 트리거 - const [isMount, setIsMount] = useState<boolean>(false); + const [isExcelDownClick, setIsExcelDownClick] = useState<boolean>(false); /** * 페이지 함수 @@ -112,7 +121,7 @@ export default function UnderWaterInput() { // 상태값 설정 setFcltCpct(selectUpdownData[0]?.val || "0"); setOperationValue(selectOperationData[0]?.val || "0"); - setSearchYear(selectSearchYear[0]?.val || "0"); + setSearchYear(defaultSearchYear); // SearchInputSelect 초기화 setFcltyName(""); if (searchInputRef.current) { @@ -121,7 +130,7 @@ export default function UnderWaterInput() { input.value = ""; } } - searchUnderWaterList(1, 10, selectSearchYear[0]?.val || "0"); + searchUnderWaterList(1, 10, defaultSearchYear); } // 모달 닫기 @@ -144,31 +153,15 @@ export default function UnderWaterInput() { useEffect(() => { if (!isInit) { initializeCommonData(); - searchUnderWaterList(1, 10, selectSearchYear[0]?.val || "0"); + searchUnderWaterList(1, 10, defaultSearchYear); } - }, [isInit, initializeCommonData, searchUnderWaterList, selectSearchYear]); - - useEffect(() => { - if(!isMount) return ; - - const timer = setTimeout(() => { - if (excelList && excelList.length > 0) { - downloadExcel({ - data: excelList as unknown as JSON[], - fileName: '지하수유입량 추정', - sheetNameArray: ['지하수유입량 추정'], - headerRange: 'A1:O1' - }); - } - }, 1000); // 약간의 지연을 두고 다운로드 실행 - return () => clearTimeout(timer); - }, [excelList, isMount]); + }, [isInit, initializeCommonData, searchUnderWaterList, defaultSearchYear]); /** * 콜백함수 */ // 조회 - const searchFacilityPrecipitation = useCallback(({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=20) => { + const searchFacilityPrecipitation = useCallback(async ({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=20) => { let nameCd, partStr switch (partValue) { case 'R': @@ -186,9 +179,10 @@ export default function UnderWaterInput() { break; } if (partStr.length > 1 || fcltCpct !== '0' || operationValue!== '0' ||nameCd ||(fcltyName.trim() !== "" && fcltyName.trim() !== null)) { - searchUnderWaterList(pageNo, pageCnt, searchYear, partStr, nameCd || '0', fcltCpct, operationValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined); + await searchUnderWaterList(pageNo, pageCnt, searchYear, partStr, nameCd || '0', fcltCpct, operationValue + , fcltyName.trim() !== ""? fcltyName : undefined); } else { - searchUnderWaterList(pageNo, pageCnt, searchYear); + await searchUnderWaterList(pageNo, pageCnt, searchYear); } }, [fcltCpct, operationValue, fcltyName, searchUnderWaterList, searchYear]); @@ -199,10 +193,67 @@ export default function UnderWaterInput() { }, [upHeadList1, upHeadList2, upHeadList3]); // 엑셀다운로드 - const excelDownload = useCallback(() => { - searchFacilityPrecipitation(ref.current, 1, 5000); - setIsMount(true); - }, [searchFacilityPrecipitation]); + const excelDownload = useCallback(async () => { + if (isExcelDownClick) return; + if (ref.current.partValue === 'A') { // + if (!ref.current.sidoValue || ref.current.sidoValue === '0') { + alert('엑셀 다운로드는 시도 조건을 선택한 후 이용 가능합니다.'); + return; + } + } else if (ref.current.partValue === 'R') { // 유역별 + if (!ref.current.regionValue || ref.current.regionValue === '0') { + alert('엑셀 다운로드는 유역 조건을 선택한 후 이용 가능합니다.'); + return; + } + } else if (ref.current.partValue === 'G') { // 환경청별 + if (!ref.current.envPartValue || ref.current.envPartValue === '0') { + alert('엑셀 다운로드는 환경청 조건을 선택한 후 이용 가능합니다.'); + return; + } + } + setIsExcelDownClick(true); + try { + await searchFacilityPrecipitation(ref.current, 1, 5000); + const latestExcelList = store.getState().UnderWater.excelList || []; + if (latestExcelList.length > 0) { + const toNumber = (value: unknown) => { + if (value === null || value === undefined || value === '') return ''; + const parsed = typeof value === "number" ? value : Number(String(value).replace(/,/g, "")); + return Number.isFinite(parsed) ? parsed : ''; + }; + + const numericKeys = [ + "시설용량(m³/일)", + "평균유입하수량(m³/일)", + "연간지하수유입량(m³/일)", + "하절기지하수유입량(m³/일)", + "동절기지하수유입량(m³/일)", + ]; + + const excelData = latestExcelList.map((row) => { + const nextRow = { ...row } as Record<string, unknown>; + numericKeys.forEach((key) => { + if (key in nextRow) { + nextRow[key] = toNumber(nextRow[key]); + } + }); + return nextRow; + }); + + downloadExcelFormat({ + data: excelData as unknown as JSON[], + fileName: '지하수유입량 추정', + sheetNameArray: ['지하수유입량 추정'], + headerRange: 'A1:O1', + decimalArray0: [9, 10, 11, 12, 13] + }); + } else { + alert("조회된 결과가 없습니다."); + } + } finally { + setIsExcelDownClick(false); + } + }, [isExcelDownClick, searchFacilityPrecipitation, store]); // 페이지 변경 const handlePageChange = useCallback((page: number) => { @@ -234,6 +285,7 @@ export default function UnderWaterInput() { btnClass={btnClass} queryClick={chkVal} excelClick={excelDownload} + excelDisabled={isExcelDownClick} initClick={resetVal} ref={ref} isAdmin={false} @@ -251,7 +303,7 @@ export default function UnderWaterInput() { {selectedRow !== null && ( <UiModal body={<UnderWaterInputModal fcltyCd={fcltyCd} fcltyName={fcltyNm} searchYear={searchYear}></UnderWaterInputModal>} - bodyClass="sm:max-w-[90vw] w-[1000px] h-[770px]" + bodyClass="sm:max-w-[90vw] w-[1000px] h-[100dvh] max-h-[770px]" open={selectedRow !== null} onOpenChange={(open) => !open && closeModal()} noHeaderYn="Y" @@ -260,4 +312,4 @@ export default function UnderWaterInput() { )} </MainDiv> ); -} \ No newline at end of file +} diff --git a/apps/fo/app/store/common/index.tsx b/apps/fo/app/store/common/index.tsx index e4aebc982..f71550f1a 100644 --- a/apps/fo/app/store/common/index.tsx +++ b/apps/fo/app/store/common/index.tsx @@ -12,6 +12,8 @@ import { getFetch, getFetchAreaList } from "@/app/components/api"; export type SelectDataType = { text: string; val: string; + selected?: boolean; + hidden?: boolean; } // grid header export type TableUpperProps = { @@ -129,14 +131,14 @@ const selectEnvPartSigunData: SelectDataType[] = [ // ]; // 상향 하향 데이터 -const selectUpdownData:SelectDataType[] = [ +const selectUpdownData: SelectDataType[] = [ { text: "전체", val: '0' }, { text: "500미만", val: '400' }, { text: "500이상", val: '500' }, ]; // 운영 상태 데이터 -const selectOperationData:SelectDataType[] = [ +const selectOperationData: SelectDataType[] = [ { text: "전체", val: '0' }, { text: "가동중", val: '1' }, { text: "재가동", val: '2' }, @@ -145,7 +147,7 @@ const selectOperationData:SelectDataType[] = [ ]; // 시설 구분 데이터 -const selectFacilityPartData:SelectDataType[] = [ +const selectFacilityPartData: SelectDataType[] = [ { text: "전체", val: '0' }, { text: "공공하수처리시설", val: 'SW' }, { text: "오수처리장", val: 'SS' }, @@ -158,25 +160,29 @@ const selectFacilityPartData:SelectDataType[] = [ // 년도 데이터 const getYearData = () => { - const year = new Date().getFullYear(); - const year1 = year - 1; - const year2 = year - 2; - const year3 = year - 3; - const year4 = year - 4; - const year5 = year - 5; - return [ - { text: year1 + "년", val: year1.toString() }, - { text: year2 + "년", val: year2.toString() }, - { text: year3 + "년", val: year3.toString() }, - { text: year4 + "년", val: year4.toString() }, - { text: year5 + "년", val: year5.toString() }, - ] + const today = new Date(); + const year = today.getFullYear(); + const month = today.getMonth() + 1; + + // 상반기(6월까지)는 2년 전, 하반기는 1년 전부터 시작 + const defaultIdx = month <= 6 ? 2 : 1; + const startYear = year; + + // 최근 10개년도 제공 (기본값: 첫 번째 항목 선택) + return Array.from({ length: 10 }, (_, index) => { + const targetYear = startYear - index; + return { + text: `${targetYear}년`, + val: targetYear.toString(), + selected: index === defaultIdx, + }; + }); } -const selectSearchYear:SelectDataType[] = getYearData(); +const selectSearchYear: SelectDataType[] = getYearData(); // 유량 기간 구분 -const selectFlowTerm : SelectDataType[] = [ +const selectFlowTerm: SelectDataType[] = [ { text: "연간", val: '0' }, { text: "하절기", val: '01' }, { text: "동절기", val: '02' }, @@ -185,15 +191,15 @@ const selectFlowTerm : SelectDataType[] = [ ]; // 유량 기간 구분 -const selectFlowPanel : SelectDataType[] = [ - { text: "전체", val: '0'}, - { text: "3~10mm미만", val: '2'}, - { text: "10~20mm미만", val: '3'}, - { text: "20~30mm미만", val: '4'}, - { text: "30~40mm미만", val: '5'}, - { text: "40~50mm미만", val: '6'}, - { text: "50~60mm미만", val: '7'}, - { text: "60mm이상", val: '8'}, +const selectFlowPanel: SelectDataType[] = [ + { text: "전체", val: '0' }, + { text: "3~10mm미만", val: '2' }, + { text: "10~20mm미만", val: '3' }, + { text: "20~30mm미만", val: '4' }, + { text: "30~40mm미만", val: '5' }, + { text: "40~50mm미만", val: '6' }, + { text: "50~60mm미만", val: '7' }, + { text: "60mm이상", val: '8' }, ]; // 중권역 데이터 @@ -220,9 +226,9 @@ export const roundUp = (value: number, precision: number = 1): number => { * @param day : 일수 * @param digit : 소수점 자리수 */ -export const qltyDataSetting = (value: number, day : number, digit: number) => { - const toZero = digit === 1 ? '0.0' : digit === 2 ? '0.00' : '0.000'; - return value > 0 ? roundUp((value/day), digit).toFixed(digit) : toZero; +export const qltyDataSetting = (value: number, day: number, digit: number) => { + const toZero = digit === 1 ? '0.0' : digit === 2 ? '0.00' : '0.000'; + return value > 0 ? roundUp((value / day), digit).toFixed(digit) : toZero; }; export const COD_YEAR = 2021; @@ -234,13 +240,13 @@ export const COD_YEAR = 2021; // 처리시설 계획정보 호출 (알에스원) export const planInfoUrl = '/dc-service/api/v1/nowDmgMtncGnsvy'; -export const fetchPlanInfo = async (targetYear : string | undefined, ctpvNm : string | undefined, sggNm : string | undefined, fcltyCd : string | undefined) => { +export const fetchPlanInfo = async (targetYear: string | undefined, ctpvNm: string | undefined, sggNm: string | undefined, fcltyCd: string | undefined) => { try { const baseUrl = planInfoUrl; const queryParams = new URLSearchParams(); if (ctpvNm && ctpvNm !== '전체') queryParams.append('ctpvNm', ctpvNm); if (sggNm && sggNm !== '전체') queryParams.append('sggNm', sggNm); - if (targetYear) queryParams.append('targetYear',targetYear); + if (targetYear) queryParams.append('targetYear', targetYear); if (fcltyCd) queryParams.append('swtrmtFcltCd', fcltyCd); const queryString = queryParams.toString(); const url = queryString ? `${baseUrl}?${queryString}` : baseUrl; @@ -258,16 +264,16 @@ const fetchSidoData = async (): Promise<SelectDataType[]> => { const res = await getFetch('fo', '/addr/city'); const data = res.data; selectSidoData = [ - { text: "전체", val: '0'}, + { text: "전체", val: '0' }, ...data.map((item: sidoDataType) => ({ - text: item.sido || '', - val: String(item.adstDstrCd) || '' + text: item.sido || '', + val: String(item.adstDstrCd) || '' })) ]; return selectSidoData; } catch (error) { - console.error('시도 데이터를 가져오는데 실패했습니다:', error); - return selectSidoData; + console.error('시도 데이터를 가져오는데 실패했습니다:', error); + return selectSidoData; } }; @@ -279,10 +285,10 @@ const fetchSigunData = async (): Promise<SelectDataType[]> => { selectSigunData = [ { text: "전체", val: '0' }, ...data - .map((item: sigunDataType) => ({ + .map((item: sigunDataType) => ({ text: item.gugun || '', val: String(item.adstDstrCd) || '' - })) + })) ]; return selectSigunData; } catch (error) { @@ -297,7 +303,7 @@ const fetchRegionData = async (): Promise<SelectDataType[]> => { const res = await getFetch('fo', '/addr/sarea'); const data = res.data; return [ - { text: "전체", val: '0'}, + { text: "전체", val: '0' }, ...data.map((item: regionDataType) => ({ text: item.rvbsn || '', val: String(item.rvbsnCd) || '' @@ -317,10 +323,10 @@ const fetchMiddleRegionData = async (): Promise<SelectDataType[]> => { return [ { text: "전체", val: '0' }, ...data - .map((item: middleRegionDataType) => ({ - text: item.mdmbsn || '', - val: String(item.rvbsnCd) || '' - })) + .map((item: middleRegionDataType) => ({ + text: item.mdmbsn || '', + val: String(item.rvbsnCd) || '' + })) ]; } catch (error) { console.error('중권역 데이터를 가져오는데 실패했습니다:', error); @@ -336,10 +342,10 @@ const fetchUnitWaterShedData = async (): Promise<SelectDataType[]> => { return [ { text: "전체", val: '0' }, ...data - .map((item: unitWaterShedDataType) => ({ - text: item.lgbsn || '', - val: String(item.rvbsnCd) || '' - })) + .map((item: unitWaterShedDataType) => ({ + text: item.lgbsn || '', + val: String(item.rvbsnCd) || '' + })) ]; } catch (error) { console.error('단위유역 데이터를 가져오는데 실패했습니다:', error); @@ -355,11 +361,11 @@ const fetchEnvPartData = async (): Promise<envPartSelectDataType[]> => { selectEnvPartData = [ { text: "전체", val: '0', adstDliStrCd: '' }, ...data - .map((item: envPartDataType) => ({ - text: item.lclgvrnmntNm || '', - val: String(item.lclgvrnmntCd) || '', - adstDliStrCd: item.adstDliStrCd || '' - })) + .map((item: envPartDataType) => ({ + text: item.lclgvrnmntNm || '', + val: String(item.lclgvrnmntCd) || '', + adstDliStrCd: item.adstDliStrCd || '' + })) ]; return selectEnvPartData; } catch (error) { @@ -372,7 +378,7 @@ const fetchEnvPartData = async (): Promise<envPartSelectDataType[]> => { * Store - expaort ====================================*/ export type CommonAction = { - getMenuAuth : (menuCd : number) => Promise<void>; + getMenuAuth: (menuCd: number) => Promise<void>; initializeCommonData: () => Promise<void>; } @@ -394,7 +400,7 @@ export type CommonType = { selectFlowPanel: SelectDataType[]; selectEnvPartData: envPartSelectDataType[]; btnClass: string; - COD_YEAR : number; + COD_YEAR: number; }; auth: { @@ -424,7 +430,7 @@ export const commonState: CommonType = { selectFlowPanel: selectFlowPanel, selectEnvPartData: selectEnvPartData, btnClass: "p-4 m-2 !mr-10 text-white rounded-sm text-lg font-medium", - COD_YEAR : COD_YEAR, + COD_YEAR: COD_YEAR, }, auth: { @@ -436,10 +442,12 @@ export const commonState: CommonType = { }, }; -export const CommonReducer:(set:(fn: (state: CommonType) => CommonType) => void)=>CommonAction=(set: (fn: (state: CommonType) => CommonType) => void) => { +export const CommonReducer = ( + set: (fn: (state: CommonType) => CommonType) => void +): CommonAction => { return { // 메뉴 권한 확인 - getMenuAuth : async (menuCd : number) =>{ + getMenuAuth: async (menuCd: number) => { const response = await getFetch( "local", `/cms-service/api/v1/menu-authrts/pageinfo/${menuCd}` @@ -478,4 +486,4 @@ export const CommonReducer:(set:(fn: (state: CommonType) => CommonType) => void) } } -export type CommonStore = CommonType & CommonAction; \ No newline at end of file +export type CommonStore = CommonType & CommonAction; diff --git a/apps/fo/app/store/dashboard/index.tsx b/apps/fo/app/store/dashboard/index.tsx index b5f678a22..398f82c0e 100644 --- a/apps/fo/app/store/dashboard/index.tsx +++ b/apps/fo/app/store/dashboard/index.tsx @@ -5,6 +5,7 @@ import { getFetch } from "@/app/components/api"; import CalcRainEffect from "@/app/components/common/CalcRainEffect"; import DataForm, { roundNumber } from "@/app/components/common/DataForm"; +// import { getDaysInMonth } from "@/app/components/common/DataForm"; import { fetchPlanInfo, OSU_PERCENT, qltyDataSetting, roundUp } from "../common"; import { type FacilityWaterQltyProps } from "../facilityWaterQlty"; import { fetchFlowRateModalList } from "../flowRate"; @@ -22,93 +23,93 @@ export type TableUpperProps = { title: string; upName?: string; upSequnce?: number; - align? : string; - row? : number; + align?: string; + row?: number; rowSpan?: number; colSpan?: number; - widthPercent? : number; + widthPercent?: number; } // ===TAB1 // main fclty export type mainFcltyProps = { - fcltyNm : string, - facilityCapacity : string, - publicMethod : string, - location : string + fcltyNm: string, + facilityCapacity: string, + publicMethod: string, + location: string } // 시설계획 export type planFcltyListProps = { - item : string, - provisionalApproval : string | number | '', - now : string | number | '', - stepOne : string | number | '', - stepTwo : string | number | '', - stepThree : string | number | '', - stepFour : string | number | '', + item: string, + provisionalApproval: string | number | '', + now: string | number | '', + stepOne: string | number | '', + stepTwo: string | number | '', + stepThree: string | number | '', + stepFour: string | number | '', } // 운영실태 export type OperationalStatusListProps = { - flowRate : string, - waterQlty : string, - yearAvg : number | '', - yearMax : number | '', - yearMin : number | '', - summerAll : number | string | '', - summerThree : number | string | '', - summerRain : number | string | '', - winterAll : number | string | '', - winterSunnyAvg : number | string | '', - winterSunnyMax : number | string | '', - winterSunnyMin : number | string | '', - groundWaterAvg : number | string | '', - groundWaterSummer : number | string | '', - groundWaterWinter : number | string | '', - rdi : number | string | '', - rdiRain : number | string | '', + flowRate: string, + waterQlty: string, + yearAvg: number | '', // 평균 + yearMax: number | '', // 최대 + yearMin: number | '', // 최소 + summerAll: number | string | '', // 하절기 전체 + summerThree: number | string | '', // 하절기 3mm이상 + summerRain: number | string | '', // 하절기 강우 + winterAll: number | string | '', // 동절기 전체 + winterSunnyAvg: number | string | '', // 동절기 청천시 평균 + winterSunnyMax: number | string | '', // 동절기 청천시 최대 + winterSunnyMin: number | string | '', // 동절기 청천시 최소 + groundWaterAvg: number | string | '', // 지하수 유입량 연평균 + groundWaterSummer: number | string | '',// 지하수 유입량 하절기 평균 + groundWaterWinter: number | string | '',// 지하수 유입량 동절기 평균 + rdi: number | string | '', // RDII 추정 + rdiRain: number | string | '', // RDII 추정시 일강수량(mm) } export interface operationalStatusListProps { - flowRate : string, - waterQlty : string, - yearAvg : number | '', - yearMax : number | '', - yearMin : number | '', - summerAll : number | '', - summerThree : number | '', - summerRain : number | '', - winterAll : number | '', - winterSunnyAvg : number | '', - winterSunnyMax : number | '', - winterSunnyMin : number | '', - groundWaterAvg : number | string | '', - groundWaterSummer : number | string | '', - groundWaterWinter : number | string | '', - rdi : number | '', + flowRate: string, + waterQlty: string, + yearAvg: number | '', + yearMax: number | '', + yearMin: number | '', + summerAll: number | '', + summerThree: number | '', + summerRain: number | '', + winterAll: number | '', + winterSunnyAvg: number | '', + winterSunnyMax: number | '', + winterSunnyMin: number | '', + groundWaterAvg: number | string | '', + groundWaterSummer: number | string | '', + groundWaterWinter: number | string | '', + rdi: number | '', } // 차트 export type lineBarChartDataProps = { - label : string, - lineVal : number - barVal : number - barVal0 : number - barVal1 : number - lineVal0 : number - lineVal1 : number + label: string, + lineVal: number + barVal: number + barVal0: number + barVal1: number + lineVal0: number + lineVal1: number } export type lineChartDataProps = { - label : string, - val : number, + label: string, + val: number, } export interface RainManageListProps { - rainItem : string, - rainRate : string, - rainDay : number, + rainItem: string, + rainRate: string, + rainDay: number, bscFctCd?: string, bscFctNm?: string, msrDt?: string @@ -126,10 +127,10 @@ export interface RainManageListProps { efflWqltSs: number | '', efflWqltTn: number | '', efflWqltTp: number | '', - rgnNm?:string|'', - rvbsn?:string|'', - lgbsn?:string|'', - mdmbsn?:string|'', + rgnNm?: string | '', + rvbsn?: string | '', + lgbsn?: string | '', + mdmbsn?: string | '', inflAmnt: number | '', lclgvrnmntNm?: string, infoRnDay?: number | '', @@ -149,7 +150,7 @@ export interface RainManageListProps { dsInflWqltTp: number | '', extnsSeqno?: number | '', rainEffectDay?: number | '', - toBeInfoRnDay? : number | '', + toBeInfoRnDay?: number | '', } export interface RainManageExcelType { @@ -157,7 +158,7 @@ export interface RainManageExcelType { 강우조건비율: number | '', 일수: number | '', 유입유량: string | number | '', - 시설용량대비: string |number | '', + 시설용량대비: string | number | '', 설계유입수질BOD: string | number | '', 설계유입수질TOC: string | number | '', 설계유입수질SS: string | number | '', @@ -169,840 +170,840 @@ export interface RainManageExcelType { 유입수질TN: string | number | '', 유입수질TP: string | number | '', 방류수질BOD: string | number | '', - 방류수질TOC: string |number | '', + 방류수질TOC: string | number | '', 방류수질SS: string | number | '', 방류수질TN: string | number | '', 방류수질TP: string | number | '', } export interface DrainageEquipmentListProps { - part1 : string | '', - part2 : string | '', - nowSum : number | '', - nowJoin : number | '', - nowSep : number | '', - stepOneSum : number | '', - stepOneJoin : number | '', - stepOneSep : number | '', - stepTwoSum : number | '', - stepTwoJoin : number | '', - stepTwoSep : number | '', - stepThreeSum : number | '', - stepThreeJoin : number | '', - stepThreeSep : number | '', - stepFourSum : number | '', - stepFourJoin : number | '', - stepFourSep : number | '', - etc : string | '', + part1: string | '', + part2: string | '', + nowSum: number | '', + nowJoin: number | '', + nowSep: number | '', + stepOneSum: number | '', + stepOneJoin: number | '', + stepOneSep: number | '', + stepTwoSum: number | '', + stepTwoJoin: number | '', + stepTwoSep: number | '', + stepThreeSum: number | '', + stepThreeJoin: number | '', + stepThreeSep: number | '', + stepFourSum: number | '', + stepFourJoin: number | '', + stepFourSep: number | '', + etc: string | '', } export interface DrainageEquipmentExcelType { - partOne : string | '', - partTwo : string | '', - nowSum : number | '', - nowJoin : number | '', - nowSep : number | '', - stepOneSum : number | '', - stepOneJoin : number | '', - stepOneSep : number | '', - stepTwoSum : number | '', - stepTwoJoin : number | '', - stepTwoSep : number | '', - stepThreeSum : number | '', - stepThreeJoin : number | '', - stepThreeSep : number | '', - stepFourSum : number | '', - stepFourJoin : number | '', - stepFourSep : number | '', + partOne: string | '', + partTwo: string | '', + nowSum: number | '', + nowJoin: number | '', + nowSep: number | '', + stepOneSum: number | '', + stepOneJoin: number | '', + stepOneSep: number | '', + stepTwoSum: number | '', + stepTwoJoin: number | '', + stepTwoSep: number | '', + stepThreeSum: number | '', + stepThreeJoin: number | '', + stepThreeSep: number | '', + stepFourSum: number | '', + stepFourJoin: number | '', + stepFourSep: number | '', } +// type WtrUsgYmProps = { +// bscFctCd: string, // 처리구역시설코드 +// levyYm: string, // 부과년월 +// wtrUsg: number, // 월별 물사용량(m³/월) +// } /** * Store - data */ // Main -const DashBoardTopLabelArray:string[] = ['구분', '시도', '시군구', '년도', '시설명']; +const DashBoardTopLabelArray: string[] = ['구분', '시도', '시군구', '년도', '시설명']; // === Tab1 // grid header -const upHeadListFclty:TableUpperProps[] = [ - { id : "fcltyNm", title : "시설명"}, - { id : "facilityCapacity", title : "시설용량(m³/일)"}, - { id : "publicMethod", title : "공법"}, - { id : "location", title : "지역구"}, +const upHeadListFclty: TableUpperProps[] = [ + { id: "fcltyNm", title: "시설명" }, + { id: "facilityCapacity", title: "시설용량(m³/일)" }, + { id: "publicMethod", title: "공법" }, + { id: "location", title: "지역구" }, ] -const fcltyList : mainFcltyProps[] = [ +const fcltyList: mainFcltyProps[] = [ ] function getPlanYearList(): string[] { - const currentYear = new Date().getFullYear(); // 올해 - const startYear = currentYear - 4; // 5년 전 - const endYear = currentYear + 1; // 내년 + const currentYear = new Date().getFullYear(); // 올해 + const startYear = currentYear - 4; // 5년 전 + const endYear = currentYear + 1; // 내년 - const years: string[] = []; + const years: string[] = []; - for (let y = startYear; y <= endYear; y++) { - years.push(String(y)); // 혹은 y.toString() - } + for (let y = startYear; y <= endYear; y++) { + years.push(String(y)); // 혹은 y.toString() + } - return years; + return years; } -let planYearList : string[] = getPlanYearList(); +let planYearList: string[] = getPlanYearList(); -const upHeadListPlanFclty : TableUpperProps[] = [ - { id: "item", title: "", upName:"upChangeRe", upSequnce:1 }, - { id: "provisionalApproval", title: "기승인\n4단계", upName:"upChangeRe", upSequnce:2 }, - { id: "now", title: "현재", upName:"upChangeRe", upSequnce:3 }, - { id: "stepOne", title: "1단계", upName:"upChangeRe", upSequnce:4 }, - { id: "stepTwo", title: "2단계", upName:"upChangeRe", upSequnce:5 }, - { id: "stepThree", title: "3단계", upName:"upChangeRe", upSequnce:6 }, - { id: "stepFour", title: "4단계", upName:"upChangeRe", upSequnce:7 }, +const upHeadListPlanFclty: TableUpperProps[] = [ + { id: "item", title: "", upName: "upChangeRe", upSequnce: 1 }, + { id: "provisionalApproval", title: "기승인\n4단계", upName: "upChangeRe", upSequnce: 2 }, + { id: "now", title: "현재", upName: "upChangeRe", upSequnce: 3 }, + { id: "stepOne", title: "1단계", upName: "upChangeRe", upSequnce: 4 }, + { id: "stepTwo", title: "2단계", upName: "upChangeRe", upSequnce: 5 }, + { id: "stepThree", title: "3단계", upName: "upChangeRe", upSequnce: 6 }, + { id: "stepFour", title: "4단계", upName: "upChangeRe", upSequnce: 7 }, ] // 시설계획 좌측 상단 -const planFcltyList:planFcltyListProps[] = [ +const planFcltyList: planFcltyListProps[] = [ { - item : "1.증설계획\n(㎥/일)", + item: "1.증설계획\n(㎥/일)", provisionalApproval: "", now: "", stepOne: "", stepTwo: "", stepThree: "", - stepFour : "", + stepFour: "", }, { - item : "2.처리인구(인)", + item: "2.처리인구(인)", provisionalApproval: "", now: "", stepOne: "", stepTwo: "", stepThree: "", - stepFour : "", + stepFour: "", }, { - item : "3.처리구역\n(km²)", + item: "3.처리구역\n(km²)", provisionalApproval: "", now: "", stepOne: "", stepTwo: "", stepThree: "", - stepFour : "", + stepFour: "", }, { - item : "4.분류식화율\n(%)", + item: "4.분류식화율\n(%)", provisionalApproval: "", now: "", stepOne: "", stepTwo: "", stepThree: "", - stepFour : "", + stepFour: "", }, { - item : "5.물사용량", + item: "5.물사용량", provisionalApproval: "", now: "", stepOne: "", stepTwo: "", stepThree: "", - stepFour : "", + stepFour: "", }, { - item : "년도\n(최근 3개년)", + item: "년도\n(최근 3개년)", provisionalApproval: "물사용량\n(㎥/일)", now: "", stepOne: "", stepTwo: "비고", stepThree: "", - stepFour : "", + stepFour: "", }, { - item : "2021", + item: "2021", provisionalApproval: "", now: "", stepOne: "", stepTwo: "", stepThree: "", - stepFour : "", + stepFour: "", }, { - item : "2022", + item: "2022", provisionalApproval: "", now: "", stepOne: "", stepTwo: "", stepThree: "", - stepFour : "", + stepFour: "", }, { - item : "2023", + item: "2023", provisionalApproval: "", now: "", stepOne: "", stepTwo: "", stepThree: "", - stepFour : "", + stepFour: "", }, ] -const upHeadListOperationStatus : TableUpperProps[] = [ - { id: "flowRate", title: "유량 및 수질", upName:"upChangeRe1", upSequnce:1 }, - { id: "waterQlty", title: "유량 및 수질", upName:"upChangeRe1", upSequnce:2 }, - { id: "yearAvg", title: "평균", upName:"upChangeRe2", upSequnce:1 }, - { id: "yearMax", title: "최대", upName:"upChangeRe2", upSequnce:2 }, - { id: "yearMin", title: "최소", upName:"upChangeRe2", upSequnce:3 }, - { id: "summerAll", title: "전기간", upName:"upChangeRe3", upSequnce:1 }, - { id: "summerThree", title: "강우시\n(3mm이상)", upName:"upChangeRe3", upSequnce:2 }, - { id: "summerRain", title: "청천시", upName:"upChangeRe3", upSequnce:3 }, - { id: "winterAll", title: "전기간", upName:"upChangeRe4", upSequnce:1 }, - { id: "winterSunnyAvg", title: "청천시\n평균", upName:"upChangeRe4", upSequnce:2 }, - { id: "winterSunnyMax", title: "청천시\n최대", upName:"upChangeRe4", upSequnce:3 }, - { id: "winterSunnyMin", title: "청천시\n최소", upName:"upChangeRe4", upSequnce:4 }, - { id: "groundWaterAvg", title: "연평균", upName:"upChangeRe5", upSequnce:1 }, - { id: "groundWaterSummer", title: "하절기\n평균", upName:"upChangeRe5", upSequnce:2 }, - { id: "groundWaterWinter", title: "동절기\n평균", upName:"upChangeRe5", upSequnce:3 }, +const upHeadListOperationStatus: TableUpperProps[] = [ // 시설정보 및 운영현황 + { id: "flowRate", title: "유량 및 수질", upName: "upChangeRe1", upSequnce: 1 }, + { id: "waterQlty", title: "유량 및 수질", upName: "upChangeRe1", upSequnce: 2 }, + { id: "yearAvg", title: "평균", upName: "upChangeRe2", upSequnce: 1 }, + { id: "yearMax", title: "최대", upName: "upChangeRe2", upSequnce: 2 }, + { id: "yearMin", title: "최소", upName: "upChangeRe2", upSequnce: 3 }, + { id: "summerAll", title: "전기간", upName: "upChangeRe3", upSequnce: 1 }, + { id: "summerThree", title: "강우시\n(3mm이상)", upName: "upChangeRe3", upSequnce: 2 }, + { id: "summerRain", title: "청천시", upName: "upChangeRe3", upSequnce: 3 }, + { id: "winterAll", title: "전기간", upName: "upChangeRe4", upSequnce: 1 }, + { id: "winterSunnyAvg", title: "청천시\n평균", upName: "upChangeRe4", upSequnce: 2 }, + { id: "winterSunnyMax", title: "청천시\n최대", upName: "upChangeRe4", upSequnce: 3 }, + { id: "winterSunnyMin", title: "청천시\n최소", upName: "upChangeRe4", upSequnce: 4 }, + { id: "groundWaterAvg", title: "연평균", upName: "upChangeRe5", upSequnce: 1 }, + { id: "groundWaterSummer", title: "하절기\n평균", upName: "upChangeRe5", upSequnce: 2 }, + { id: "groundWaterWinter", title: "동절기\n평균", upName: "upChangeRe5", upSequnce: 3 }, { id: "rdi", title: "RDII 추정" }, { id: "rdiRain", title: "RDII 추정시\n일강수량(mm)" }, ] -const upHeadListRainManage : TableUpperProps[] = [ - { id: "rainItem", title: "강우량", row:0, rowSpan:2, widthPercent: 8 }, - { id: "rainRate", title: "강우조건\n비율(%)", row:0, rowSpan:2, align: "right", widthPercent: 5 }, - { id: "rainDay", title: "일수", row:0, rowSpan:2, align: "right", widthPercent: 5 }, - { id: "A", title: "유입(㎥/일)", row:0, colSpan:2 }, - { id: "inflAmnt", title: "유입유량", row:1, align: "right", widthPercent: 6 }, - { id: "fctCpc", title: "시설용량대비", row:1, align: "right", widthPercent: 6 }, - { id: "B", title: "설계유입수질(㎎/L)", row:0, colSpan:5 }, - { id: "dsInflWqltBod", title: "BOD", row:1, align: "right" }, - { id: "dsInflWqltCod", title: "TOC\n(COD)", row:1, align: "right" }, - { id: "dsInflWqltSs", title: "SS", row:1, align: "right" }, - { id: "dsInflWqltTn", title: "T-N", row:1, align: "right" }, - { id: "dsInflWqltTp", title: "T-P", row:1, align: "right" }, - { id: "C", title: "유입수질(㎎/L)", row:0, colSpan:5 }, - { id: "inflAssctBfrWqltBod", title: "BOD", row:1, align: "right" }, - { id: "inflAssctBfrWqltToc", title: "TOC\n(COD)", row:1, align: "right" }, - { id: "inflAssctBfrWqltSs", title: "SS", row:1, align: "right" }, - { id: "inflAssctBfrWqltTn", title: "T-N", row:1, align: "right" }, - { id: "inflAssctBfrWqltTp", title: "T-P", row:1, align: "right" }, - { id: "D", title: "방류수질(㎎/L)", row:0, colSpan:5 }, - { id: "efflWqltBod", title: "BOD", row:1, align: "right" }, - { id: "efflWqltToc", title: "TOC\n(COD)", row:1, align: "right" }, - { id: "efflWqltSs", title: "SS", row:1, align: "right" }, - { id: "efflWqltTn", title: "T-N", row:1, align: "right" }, - { id: "efflWqltTp", title: "T-P", row:1, align: "right" }, +const upHeadListRainManage: TableUpperProps[] = [ // 강우일 운영현황 + { id: "rainItem", title: "강우량", row: 0, rowSpan: 2, widthPercent: 8 }, + { id: "rainRate", title: "강우조건\n비율(%)", row: 0, rowSpan: 2, align: "right", widthPercent: 5 }, + { id: "rainDay", title: "일수", row: 0, rowSpan: 2, align: "right", widthPercent: 5 }, + { id: "A", title: "유입(㎥/일)", row: 0, colSpan: 2 }, + { id: "inflAmnt", title: "유입유량", row: 1, align: "right", widthPercent: 6 }, + { id: "fctCpc", title: "시설용량대비", row: 1, align: "right", widthPercent: 6 }, + { id: "B", title: "설계유입수질(㎎/L)", row: 0, colSpan: 5 }, + { id: "dsInflWqltBod", title: "BOD", row: 1, align: "right" }, + { id: "dsInflWqltCod", title: "TOC\n(COD)", row: 1, align: "right" }, + { id: "dsInflWqltSs", title: "SS", row: 1, align: "right" }, + { id: "dsInflWqltTn", title: "T-N", row: 1, align: "right" }, + { id: "dsInflWqltTp", title: "T-P", row: 1, align: "right" }, + { id: "C", title: "유입수질(㎎/L)", row: 0, colSpan: 5 }, + { id: "inflAssctBfrWqltBod", title: "BOD", row: 1, align: "right" }, + { id: "inflAssctBfrWqltToc", title: "TOC\n(COD)", row: 1, align: "right" }, + { id: "inflAssctBfrWqltSs", title: "SS", row: 1, align: "right" }, + { id: "inflAssctBfrWqltTn", title: "T-N", row: 1, align: "right" }, + { id: "inflAssctBfrWqltTp", title: "T-P", row: 1, align: "right" }, + { id: "D", title: "방류수질(㎎/L)", row: 0, colSpan: 5 }, + { id: "efflWqltBod", title: "BOD", row: 1, align: "right" }, + { id: "efflWqltToc", title: "TOC\n(COD)", row: 1, align: "right" }, + { id: "efflWqltSs", title: "SS", row: 1, align: "right" }, + { id: "efflWqltTn", title: "T-N", row: 1, align: "right" }, + { id: "efflWqltTp", title: "T-P", row: 1, align: "right" }, ] -const upHeadListDrainageEquipment : TableUpperProps[] = [ - { id: "part1", title: "구분", row:0, upName:"upChangeRe1", upSequnce:1 }, - { id: "part2", title: "구분", row:0, upName:"upChangeRe1", upSequnce:2 }, - { id: "nowSum", title: "계", row:2, upName:"upChangeRe2", upSequnce:1 }, - { id: "nowJoin", title: "합류식\n가구수", row:2, upName:"upChangeRe2", upSequnce:2 }, - { id: "nowSep", title: "분리식\n가구수", row:2, upName:"upChangeRe2", upSequnce:3 }, - { id: "stepOneSum", title: "계", row:2, upName:"upChangeRe3", upSequnce:1 }, - { id: "stepOneJoin", title: "합류식\n가구수", row:2, upName:"upChangeRe3", upSequnce:2 }, - { id: "stepOneSep", title: "분리식\n가구수", row:2, upName:"upChangeRe3", upSequnce:3 }, - { id: "stepTwoSum", title: "계", row:2, upName:"upChangeRe4", upSequnce:1 }, - { id: "stepTwoJoin", title: "합류식\n가구수", row:2, upName:"upChangeRe4", upSequnce:2 }, - { id: "stepTwoSep", title: "분리식\n가구수", row:2, upName:"upChangeRe4", upSequnce:3 }, - { id: "stepThreeSum", title: "계", row:2, upName:"upChangeRe5", upSequnce:1 }, - { id: "stepThreeJoin", title: "합류식\n가구수", row:2, upName:"upChangeRe5", upSequnce:2 }, - { id: "stepThreeSep", title: "분리식\n가구수", row:2, upName:"upChangeRe5", upSequnce:3 }, - { id: "stepFourSum", title: "계", row:2, upName:"upChangeRe6", upSequnce:1 }, - { id: "stepFourJoin", title: "합류식\n가구수", row:2, upName:"upChangeRe6", upSequnce:2 }, - { id: "stepFourSep", title: "분리식\n가구수", row:2, upName:"upChangeRe6", upSequnce:3 }, - { id: "etc", title: "비고", row:0, upName:"upChangeRe7", upSequnce:1 }, +const upHeadListDrainageEquipment: TableUpperProps[] = [ + { id: "part1", title: "구분", row: 0, upName: "upChangeRe1", upSequnce: 1 }, + { id: "part2", title: "구분", row: 0, upName: "upChangeRe1", upSequnce: 2 }, + { id: "nowSum", title: "계", row: 2, upName: "upChangeRe2", upSequnce: 1 }, + { id: "nowJoin", title: "합류식\n가구수", row: 2, upName: "upChangeRe2", upSequnce: 2 }, + { id: "nowSep", title: "분리식\n가구수", row: 2, upName: "upChangeRe2", upSequnce: 3 }, + { id: "stepOneSum", title: "계", row: 2, upName: "upChangeRe3", upSequnce: 1 }, + { id: "stepOneJoin", title: "합류식\n가구수", row: 2, upName: "upChangeRe3", upSequnce: 2 }, + { id: "stepOneSep", title: "분리식\n가구수", row: 2, upName: "upChangeRe3", upSequnce: 3 }, + { id: "stepTwoSum", title: "계", row: 2, upName: "upChangeRe4", upSequnce: 1 }, + { id: "stepTwoJoin", title: "합류식\n가구수", row: 2, upName: "upChangeRe4", upSequnce: 2 }, + { id: "stepTwoSep", title: "분리식\n가구수", row: 2, upName: "upChangeRe4", upSequnce: 3 }, + { id: "stepThreeSum", title: "계", row: 2, upName: "upChangeRe5", upSequnce: 1 }, + { id: "stepThreeJoin", title: "합류식\n가구수", row: 2, upName: "upChangeRe5", upSequnce: 2 }, + { id: "stepThreeSep", title: "분리식\n가구수", row: 2, upName: "upChangeRe5", upSequnce: 3 }, + { id: "stepFourSum", title: "계", row: 2, upName: "upChangeRe6", upSequnce: 1 }, + { id: "stepFourJoin", title: "합류식\n가구수", row: 2, upName: "upChangeRe6", upSequnce: 2 }, + { id: "stepFourSep", title: "분리식\n가구수", row: 2, upName: "upChangeRe6", upSequnce: 3 }, + { id: "etc", title: "비고", row: 0, upName: "upChangeRe7", upSequnce: 1 }, ] -const operationalStatusListOrg : OperationalStatusListProps[] = [ +const operationalStatusListOrg: OperationalStatusListProps[] = [ { - flowRate : '유량\n(㎥/일)', - waterQlty : '유입', - yearAvg : '', - yearMax : '', - yearMin : '', - summerAll : '', - summerThree : '', - summerRain : '', - winterAll : '', - winterSunnyAvg : '', - winterSunnyMax : '', - winterSunnyMin : '', - groundWaterAvg : '', - groundWaterSummer : '', - groundWaterWinter : '', - rdi : '', - rdiRain : '', + flowRate: '유량\n(㎥/일)', + waterQlty: '유입', + yearAvg: '', + yearMax: '', + yearMin: '', + summerAll: '', + summerThree: '', + summerRain: '', + winterAll: '', + winterSunnyAvg: '', + winterSunnyMax: '', + winterSunnyMin: '', + groundWaterAvg: '', + groundWaterSummer: '', + groundWaterWinter: '', + rdi: '', + rdiRain: '', }, { - flowRate : '유량\n(㎥/일)', - waterQlty : '순수하수', - yearAvg : '', - yearMax : '', - yearMin : '', - summerAll : '', - summerThree : '', - summerRain : '', - winterAll : '', - winterSunnyAvg : '', - winterSunnyMax : '', - winterSunnyMin : '', - groundWaterAvg : '', - groundWaterSummer : '', - groundWaterWinter : '', - rdi : '', - rdiRain : '', + flowRate: '유량\n(㎥/일)', + waterQlty: '순수하수', + yearAvg: '', + yearMax: '', + yearMin: '', + summerAll: '', + summerThree: '', + summerRain: '', + winterAll: '', + winterSunnyAvg: '', + winterSunnyMax: '', + winterSunnyMin: '', + groundWaterAvg: '', + groundWaterSummer: '', + groundWaterWinter: '', + rdi: '', + rdiRain: '', }, { - flowRate : '유량\n(㎥/일)', - waterQlty : '연계처리수', - yearAvg : '', - yearMax : '', - yearMin : '', - summerAll : '', - summerThree : '', - summerRain : '', - winterAll : '', - winterSunnyAvg : '', - winterSunnyMax : '', - winterSunnyMin : '', - groundWaterAvg : '', - groundWaterSummer : '', - groundWaterWinter : '', - rdi : '', - rdiRain : '', + flowRate: '유량\n(㎥/일)', + waterQlty: '연계처리수', + yearAvg: '', + yearMax: '', + yearMin: '', + summerAll: '', + summerThree: '', + summerRain: '', + winterAll: '', + winterSunnyAvg: '', + winterSunnyMax: '', + winterSunnyMin: '', + groundWaterAvg: '', + groundWaterSummer: '', + groundWaterWinter: '', + rdi: '', + rdiRain: '', }, { - flowRate : '유량\n(㎥/일)', - waterQlty : '총인설비\n반류수', - yearAvg : '', - yearMax : '', - yearMin : '', - summerAll : '', - summerThree : '', - summerRain : '', - winterAll : '', - winterSunnyAvg : '', - winterSunnyMax : '', - winterSunnyMin : '', - groundWaterAvg : '', - groundWaterSummer : '', - groundWaterWinter : '', - rdi : '', - rdiRain : '', + flowRate: '유량\n(㎥/일)', + waterQlty: '총인설비\n반류수', + yearAvg: '', + yearMax: '', + yearMin: '', + summerAll: '', + summerThree: '', + summerRain: '', + winterAll: '', + winterSunnyAvg: '', + winterSunnyMax: '', + winterSunnyMin: '', + groundWaterAvg: '', + groundWaterSummer: '', + groundWaterWinter: '', + rdi: '', + rdiRain: '', }, { - flowRate : '유량\n(㎥/일)', - waterQlty : '방류', - yearAvg : '', - yearMax : '', - yearMin : '', - summerAll : '', - summerThree : '', - summerRain : '', - winterAll : '', - winterSunnyAvg : '', - winterSunnyMax : '', - winterSunnyMin : '', - groundWaterAvg : '', - groundWaterSummer : '', - groundWaterWinter : '', - rdi : '', - rdiRain : '', + flowRate: '유량\n(㎥/일)', + waterQlty: '방류', + yearAvg: '', + yearMax: '', + yearMin: '', + summerAll: '', + summerThree: '', + summerRain: '', + winterAll: '', + winterSunnyAvg: '', + winterSunnyMax: '', + winterSunnyMin: '', + groundWaterAvg: '', + groundWaterSummer: '', + groundWaterWinter: '', + rdi: '', + rdiRain: '', }, { - flowRate : '수질\n(mg/L)', - waterQlty : '유입\n(BOD)', - yearAvg : '', - yearMax : '', - yearMin : '', - summerAll : '', - summerThree : '', - summerRain : '', - winterAll : '', - winterSunnyAvg : '', - winterSunnyMax : '', - winterSunnyMin : '', - groundWaterAvg : '', - groundWaterSummer : '', - groundWaterWinter : '', - rdi : '', - rdiRain : '', + flowRate: '수질\n(mg/L)', + waterQlty: '유입\n(BOD)', + yearAvg: '', + yearMax: '', + yearMin: '', + summerAll: '', + summerThree: '', + summerRain: '', + winterAll: '', + winterSunnyAvg: '', + winterSunnyMax: '', + winterSunnyMin: '', + groundWaterAvg: '', + groundWaterSummer: '', + groundWaterWinter: '', + rdi: '', + rdiRain: '', }, { - flowRate : '수질\n(mg/L)', - waterQlty : '방류\n(BOD)', - yearAvg : '', - yearMax : '', - yearMin : '', - summerAll : '', - summerThree : '', - summerRain : '', - winterAll : '', - winterSunnyAvg : '', - winterSunnyMax : '', - winterSunnyMin : '', - groundWaterAvg : '', - groundWaterSummer : '', - groundWaterWinter : '', - rdi : '', - rdiRain : '', + flowRate: '수질\n(mg/L)', + waterQlty: '방류\n(BOD)', + yearAvg: '', + yearMax: '', + yearMin: '', + summerAll: '', + summerThree: '', + summerRain: '', + winterAll: '', + winterSunnyAvg: '', + winterSunnyMax: '', + winterSunnyMin: '', + groundWaterAvg: '', + groundWaterSummer: '', + groundWaterWinter: '', + rdi: '', + rdiRain: '', }, { - flowRate : '수질\n(mg/L)', - waterQlty : '유입\n(T-N)', - yearAvg : '', - yearMax : '', - yearMin : '', - summerAll : '', - summerThree : '', - summerRain : '', - winterAll : '', - winterSunnyAvg : '', - winterSunnyMax : '', - winterSunnyMin : '', - groundWaterAvg : '', - groundWaterSummer : '', - groundWaterWinter : '', - rdi : '', - rdiRain : '', + flowRate: '수질\n(mg/L)', + waterQlty: '유입\n(T-N)', + yearAvg: '', + yearMax: '', + yearMin: '', + summerAll: '', + summerThree: '', + summerRain: '', + winterAll: '', + winterSunnyAvg: '', + winterSunnyMax: '', + winterSunnyMin: '', + groundWaterAvg: '', + groundWaterSummer: '', + groundWaterWinter: '', + rdi: '', + rdiRain: '', }, { - flowRate : '수질\n(mg/L)', - waterQlty : '방류\n(T-N)', - yearAvg : '', - yearMax : '', - yearMin : '', - summerAll : '', - summerThree : '', - summerRain : '', - winterAll : '', - winterSunnyAvg : '', - winterSunnyMax : '', - winterSunnyMin : '', - groundWaterAvg : '', - groundWaterSummer : '', - groundWaterWinter : '', - rdi : '', - rdiRain : '', + flowRate: '수질\n(mg/L)', + waterQlty: '방류\n(T-N)', + yearAvg: '', + yearMax: '', + yearMin: '', + summerAll: '', + summerThree: '', + summerRain: '', + winterAll: '', + winterSunnyAvg: '', + winterSunnyMax: '', + winterSunnyMin: '', + groundWaterAvg: '', + groundWaterSummer: '', + groundWaterWinter: '', + rdi: '', + rdiRain: '', }, { - flowRate : '수질\n(mg/L)', - waterQlty : '유입\n(T-P)', - yearAvg : '', - yearMax : '', - yearMin : '', - summerAll : '', - summerThree : '', - summerRain : '', - winterAll : '', - winterSunnyAvg : '', - winterSunnyMax : '', - winterSunnyMin : '', - groundWaterAvg : '', - groundWaterSummer : '', - groundWaterWinter : '', - rdi : '', - rdiRain : '', + flowRate: '수질\n(mg/L)', + waterQlty: '유입\n(T-P)', + yearAvg: '', + yearMax: '', + yearMin: '', + summerAll: '', + summerThree: '', + summerRain: '', + winterAll: '', + winterSunnyAvg: '', + winterSunnyMax: '', + winterSunnyMin: '', + groundWaterAvg: '', + groundWaterSummer: '', + groundWaterWinter: '', + rdi: '', + rdiRain: '', }, { - flowRate : '수질\n(mg/L)', - waterQlty : '방류\n(T-P)', - yearAvg : '', - yearMax : '', - yearMin : '', - summerAll : '', - summerThree : '', - summerRain : '', - winterAll : '', - winterSunnyAvg : '', - winterSunnyMax : '', - winterSunnyMin : '', - groundWaterAvg : '', - groundWaterSummer : '', - groundWaterWinter : '', - rdi : '', - rdiRain : '', + flowRate: '수질\n(mg/L)', + waterQlty: '방류\n(T-P)', + yearAvg: '', + yearMax: '', + yearMin: '', + summerAll: '', + summerThree: '', + summerRain: '', + winterAll: '', + winterSunnyAvg: '', + winterSunnyMax: '', + winterSunnyMin: '', + groundWaterAvg: '', + groundWaterSummer: '', + groundWaterWinter: '', + rdi: '', + rdiRain: '', }, { - flowRate : '총인\n시설\n수질\n(mg/L)', - waterQlty : '총인설비\n유입(T-P)', - yearAvg : '', - yearMax : '', - yearMin : '', - summerAll : '', - summerThree : '', - summerRain : '', - winterAll : '', - winterSunnyAvg : '', - winterSunnyMax : '', - winterSunnyMin : '', - groundWaterAvg : '', - groundWaterSummer : '', - groundWaterWinter : '', - rdi : '', - rdiRain : '', + flowRate: '총인\n시설\n수질\n(mg/L)', + waterQlty: '총인설비\n유입(T-P)', + yearAvg: '', + yearMax: '', + yearMin: '', + summerAll: '', + summerThree: '', + summerRain: '', + winterAll: '', + winterSunnyAvg: '', + winterSunnyMax: '', + winterSunnyMin: '', + groundWaterAvg: '', + groundWaterSummer: '', + groundWaterWinter: '', + rdi: '', + rdiRain: '', }, { - flowRate : '총인\n시설\n수질\n(mg/L)', - waterQlty : '총인설비\n방류(T-P)', - yearAvg : '', - yearMax : '', - yearMin : '', - summerAll : '', - summerThree : '', - summerRain : '', - winterAll : '', - winterSunnyAvg : '', - winterSunnyMax : '', - winterSunnyMin : '', - groundWaterAvg : '', - groundWaterSummer : '', - groundWaterWinter : '', - rdi : '', - rdiRain : '', + flowRate: '총인\n시설\n수질\n(mg/L)', + waterQlty: '총인설비\n방류(T-P)', + yearAvg: '', + yearMax: '', + yearMin: '', + summerAll: '', + summerThree: '', + summerRain: '', + winterAll: '', + winterSunnyAvg: '', + winterSunnyMax: '', + winterSunnyMin: '', + groundWaterAvg: '', + groundWaterSummer: '', + groundWaterWinter: '', + rdi: '', + rdiRain: '', }, ] -const chart_inflowRain : lineBarChartDataProps[] = []; -const chart_bodRain : lineBarChartDataProps[] = []; -const chart_inflTp : lineChartDataProps[] = []; -const chart_chongInTp : lineChartDataProps[] = []; -const chart_efflTp : lineChartDataProps[] = []; -const chart_inflTn : lineChartDataProps[] = []; -const chart_efflTn : lineChartDataProps[] = []; -const chart_inflBod : lineChartDataProps[] = []; -const chart_efflBod : lineChartDataProps[] = []; -const waterQltyStandartd : any = []; +const chart_inflowRain: lineBarChartDataProps[] = []; +const chart_bodRain: lineBarChartDataProps[] = []; +const chart_inflTp: lineChartDataProps[] = []; +const chart_chongInTp: lineChartDataProps[] = []; +const chart_efflTp: lineChartDataProps[] = []; +const chart_inflTn: lineChartDataProps[] = []; +const chart_efflTn: lineChartDataProps[] = []; +const chart_inflBod: lineChartDataProps[] = []; +const chart_efflBod: lineChartDataProps[] = []; +const waterQltyStandartd: any = []; -const rainManageList : RainManageListProps[] = []; +const rainManageList: RainManageListProps[] = []; -const drainageEquipmentList : DrainageEquipmentListProps[] = [ +const drainageEquipmentList: DrainageEquipmentListProps[] = [ { - part1 : '청천시청', - part2 : '청천시청', - nowSum : 101, - nowJoin : 102, - nowSep : 103, - stepOneSum : 104, - stepOneJoin : 105, - stepOneSep : 106, - stepTwoSum : 107, - stepTwoJoin : 108, - stepTwoSep : 109, - stepThreeSum : 110, - stepThreeJoin : 111, - stepThreeSep : 112, - stepFourSum : 113, - stepFourJoin : 114, - stepFourSep : 115, - etc : '1050', + part1: '청천시청', + part2: '청천시청', + nowSum: 101, + nowJoin: 102, + nowSep: 103, + stepOneSum: 104, + stepOneJoin: 105, + stepOneSep: 106, + stepTwoSum: 107, + stepTwoJoin: 108, + stepTwoSep: 109, + stepThreeSum: 110, + stepThreeJoin: 111, + stepThreeSep: 112, + stepFourSum: 113, + stepFourJoin: 114, + stepFourSep: 115, + etc: '1050', } ]; +type ApiRes = { data: any; count: any; } /**==================================== * Store - api (데이터 호출) ====================================*/ /* 시설정보 및 운영현황 - 계획 */ -const fetchFacilityPlan = async (targetYear : string | undefined, fcltyCd : string | undefined, set : (fn: (state: DashBoardType) => DashBoardType) => void) => { - try { - const res = await fetchPlanInfo(undefined, undefined, undefined, fcltyCd); - return res; - } catch (error) { - console.error('시설계획정보를 가져오는데 실패했습니다:', error); - set((state) => ({ - ...state, - PlanFclty : { - ...state.PlanFclty, - isLoadingPlanFclty : false, - isLoadingOperationStatus : false - } - })); - return {data: [], count: 0}; - } -}; - -/* 시설정보 및 운영현황 - 운영실태 */ -const fetchOperationStatus = async (searchYear: string|undefined, fcltyCd: string|undefined) => { - try{ - const url = '/dashBoard/mngInfo/' + (searchYear?searchYear:'2023') + '/' + (fcltyCd?fcltyCd:''); - const res = await getFetch('fo', url); - return { data: res.data, count: res.count } - } catch (error) { - console.error('운영실태 데이터를 가져오는데 실패했습니다:', error); +const fetchFacilityPlan = async (targetYear: string | undefined, fcltyCd: string | undefined, set: (fn: (state: DashBoardType) => DashBoardType) => void) => { + try { + const res = await fetchPlanInfo(undefined, undefined, undefined, fcltyCd); + return res; + } catch (error) { + console.error('시설계획정보를 가져오는데 실패했습니다:', error); + set((state) => ({ + ...state, + PlanFclty: { + ...state.PlanFclty, + isLoadingPlanFclty: false, + isLoadingOperationStatus: false + } + })); return { data: [], count: 0 }; } -} +}; + +// 시설들 월별 물사용량 정보 조회 +// const fetchWtrUsgYmList = async (searchYear: string | undefined, fcltCdList: string[]): Promise<WtrUsgYmProps | null> => { +// try { +// const res = await postFetch('fo', '/fclt/flow/wtrUsgYm/' + searchYear, fcltCdList); +// return res; +// } catch (error) { +// console.error('시설들 월별 물사용량 데이터를 가져오는데 실패했습니다:', error); +// return null; +// } +// }; // 주요그래프 데이터 -const fetchMainGraph = async (searchYear: string|undefined, fcltyCd: string|undefined) => { - try { - const url = '/fclt/qlty/hist/' + (searchYear?searchYear:'2023') + '/' + (fcltyCd?fcltyCd:'11680PB001R'); +const fetchMainGraph = async (searchYear: string | undefined, fcltyCd: string | undefined) => { + try { + const url = '/fclt/qlty/hist/' + (searchYear ? searchYear : '2023') + '/' + (fcltyCd ? fcltyCd : '11680PB001R'); const res = await getFetch('fo', url); return { data: res.data, count: res.count } - } catch (error) { + } catch (error) { console.error('주요그래프 데이터를 가져오는데 실패했습니다:', error); return { data: [], count: 0 }; } } // 수질 데이터 청천시, 강우시 재계산 -const calcWaterQltyData = async (data: any[], searchYear : string|undefined, fcltyCd : string|undefined) => { +const calcWaterQltyData = async (data: any[], searchYear: string | undefined, fcltyCd: string | undefined, mainGraphData: any) => { + const operList = { + inflBod: { yearTot: 0, yearAvg: 0, yearMax: 0, yearMin: 9999999 }, // 유입BOD + inflTN: { yearTot: 0, yearAvg: 0, yearMax: 0, yearMin: 9999999 }, // 유입TN + inflTP: { yearTot: 0, yearAvg: 0, yearMax: 0, yearMin: 9999999 }, // 유입TP + efflBod: { yearTot: 0, yearAvg: 0, yearMax: 0, yearMin: 9999999 }, // 방류BOD + efflTN: { yearTot: 0, yearAvg: 0, yearMax: 0, yearMin: 9999999 }, // 방류TN + efflTP: { yearTot: 0, yearAvg: 0, yearMax: 0, yearMin: 9999999 }, // 방류TP + tpInflTp: { yearTot: 0, yearAvg: 0, yearMax: 0, yearMin: 9999999 }, //총인 유입수질 TP(mg/L) + }; const nullToZero = (value: any) => { return value === null || value === undefined || value === '' ? 0 : Number(value); }; const orgData = data; - const res = await fetchMainGraph(searchYear, fcltyCd); - if(res.data.wqltObjs.length> 0) { - const clone = (arr: any[]) => arr?.map(v => ({ ...v })) ?? []; + // const res = await fetchMainGraph(searchYear, fcltyCd); // /fclt/qlty/hist/{year}/{fcltyCd} + const res = mainGraphData; + if (res.data.wqltObjs.length > 0) { + // const clone = (arr: any[]) => arr?.map(v => ({ ...v })) ?? []; const qltyData = res.data.wqltObjs; - const qltyChungchunData = CalcRainEffect(clone(qltyData), 'SUNNY'); - const qltyRainData = CalcRainEffect(clone(qltyData), 'RAIN'); + // const qltyChungchunData = CalcRainEffect(clone(qltyData), 'SUNNY'); + + // 강우시 변수 + const rainEffectDayArray: string[] = []; // (0) 전체변수 - let summerDay = 0; - let noNullSummerDay = 0; - let totalInflBodAvgSummer = 0; - let totalInflTnAvgSummer = 0; - let totalInflTpAvgSummer = 0; - let totalEfflBodAvgSummer = 0; - let totalEfflTnAvgSummer = 0; - let totalEfflTpAvgSummer = 0; - let totalTpInflAvgSummer = 0; + let summerDay = 0, noNullSummerDay = 0; + let totalInflBodAvgSummer = 0, totalInflTnAvgSummer = 0, totalInflTpAvgSummer = 0; + let totalEfflBodAvgSummer = 0, totalEfflTnAvgSummer = 0, totalEfflTpAvgSummer = 0, totalTpInflAvgSummer = 0; - let winterDay = 0; - let noNullWinterDay = 0; - let totalInflBodAvgWinter = 0; - let totalInflTnAvgWinter = 0; - let totalInflTpAvgWinter = 0; - let totalEfflBodAvgWinter = 0; - let totalEfflTnAvgWinter = 0; - let totalEfflTpAvgWinter = 0; - let totalTpInflAvgWinter = 0; - - // 전체 대상 - qltyData.forEach((item: any) => { - const month = item.msrDt.substring(4, 6); // 월 추출 - if(month === '01' || month === '02'){ - winterDay += 1; - if(item.tpInflWqltTp != null && item.tpInflWqltTp != 0){ - noNullWinterDay+=1; - } - totalInflBodAvgWinter += nullToZero(item.inflAssctBfrWqltBod); - totalInflTnAvgWinter += nullToZero(item.inflAssctBfrWqltTn); - totalInflTpAvgWinter += nullToZero(item.inflAssctBfrWqltTp); - totalEfflBodAvgWinter += nullToZero(item.efflWqltBod); - totalEfflTnAvgWinter += nullToZero(item.efflWqltTn); - totalEfflTpAvgWinter += nullToZero(item.efflWqltTp); - totalTpInflAvgWinter += nullToZero(item.tpInflWqltTp); - }else if(month === '06' || month === '07' || month === '08'){ - summerDay += 1; - if(item.tpInflWqltTp != null && item.tpInflWqltTp != 0){ - noNullSummerDay+=1; - } - totalInflBodAvgSummer += nullToZero(item.inflAssctBfrWqltBod); - totalInflTnAvgSummer += nullToZero(item.inflAssctBfrWqltTn); - totalInflTpAvgSummer += nullToZero(item.inflAssctBfrWqltTp); - totalEfflBodAvgSummer += nullToZero(item.efflWqltBod); - totalEfflTnAvgSummer += nullToZero(item.efflWqltTn); - totalEfflTpAvgSummer += nullToZero(item.efflWqltTp); - totalTpInflAvgSummer += nullToZero(item.tpInflWqltTp); - } - }); - - orgData[5].winterAll = qltyDataSetting(totalInflBodAvgWinter, winterDay, 1); // 동절기 유입BOD - orgData[6].winterAll = qltyDataSetting(totalEfflBodAvgWinter, winterDay, 1); // 동절기 방류BOD - orgData[7].winterAll = qltyDataSetting(totalInflTnAvgWinter, winterDay, 3); // 동절기 유입TN - orgData[8].winterAll = qltyDataSetting(totalEfflTnAvgWinter, winterDay, 3); // 동절기 방류TN - orgData[9].winterAll = qltyDataSetting(totalInflTpAvgWinter, winterDay, 3); // 동절기 유입TP - orgData[10].winterAll = qltyDataSetting(totalEfflTpAvgWinter, winterDay, 3); // 동절기 방류TP - - //총인설비 - orgData[11].winterAll = qltyDataSetting(totalTpInflAvgWinter, noNullWinterDay, 3); - orgData[12].winterAll = qltyDataSetting(totalEfflTpAvgWinter, winterDay, 3); - - orgData[5].summerAll = qltyDataSetting(totalInflBodAvgSummer, summerDay, 1); // 하절기 유입BOD - orgData[6].summerAll = qltyDataSetting(totalEfflBodAvgSummer, summerDay, 1); // 하절기 방류BOD - orgData[7].summerAll = qltyDataSetting(totalInflTnAvgSummer, summerDay, 3); // 하절기 유입TN - orgData[8].summerAll = qltyDataSetting(totalEfflTnAvgSummer, summerDay, 3); // 하절기 방류TN - orgData[9].summerAll = qltyDataSetting(totalInflTpAvgSummer, summerDay, 3); // 하절기 유입TP - orgData[10].summerAll = qltyDataSetting(totalEfflTpAvgSummer, summerDay, 3); // 하절기 방류TP - - //총인설비 - orgData[11].summerAll = qltyDataSetting(totalTpInflAvgSummer, noNullSummerDay, 3); - orgData[12].summerAll = qltyDataSetting(totalEfflTpAvgSummer, summerDay, 3); + let winterDay = 0, noNullWinterDay = 0; + let totalInflBodAvgWinter = 0, totalInflTnAvgWinter = 0, totalInflTpAvgWinter = 0; + let totalEfflBodAvgWinter = 0, totalEfflTnAvgWinter = 0, totalEfflTpAvgWinter = 0, totalTpInflAvgWinter = 0; // (1) 청천시 변수 // (1-1) 날짜 count - let chungchunSummerDay = 0; - let chungchunWinterDay = 0; - let noNullChungchunSummerDay = 0; - let noNullChungchunWinterDay = 0; + + let chungchunSummerDay = 0, noNullChungchunSummerDay = 0; + let chungchunWinterDay = 0, noNullChungchunWinterDay = 0; // (1-2) 하절기 - let chungchunInflBodAvgSummer = 0; // 하절기 청천시 유입BOD - let chungchunInflTnAvgSummer = 0; // 하절기 청천시 유입TN - let chungchunInflTpAvgSummer = 0; // 하절기 청천시 유입TP - let chungchunEfflBodAvgSummer = 0; // 하절기 청천시 방류BOD - let chungchunEfflTnAvgSummer = 0; // 하절기 청천시 방류TN - let chungchunEfflTpAvgSummer = 0; // 하절기 청천시 방류TP - let chungchunTpInflAvgSummer = 0; // 하절기 청천시 총인설비 유입TP + let chungchunInflBodAvgSummer = 0, chungchunInflTnAvgSummer = 0, chungchunInflTpAvgSummer = 0; + let chungchunEfflBodAvgSummer = 0, chungchunEfflTnAvgSummer = 0, chungchunEfflTpAvgSummer = 0; + let chungchunTpInflAvgSummer = 0; // 하절기 청천시 총인설비 유입TP // (1-3) 동절기 (최대, 최소, 평균 다 필요) - let chungchunInflBodAvg = 0; - let chungchunInflTnAvg = 0; - let chungchunInflTpAvg = 0; - let chungchunTpInflAvg = 0; - let chungchunInflBodMax = 0; - let chungchunInflTnMax = 0; - let chungchunInflTpMax = 0; - let chungchunTpInflMax = 0; + let chungchunInflBodAvg = 0, chungchunInflTnAvg = 0, chungchunInflTpAvg = 0, chungchunTpInflAvg = 0; + let chungchunInflBodMax = 0, chungchunInflTnMax = 0, chungchunInflTpMax = 0, chungchunTpInflMax = 0; let chungchunInflBodMin = 99999999; - let chungchunInflTnMin = 99999999; - let chungchunInflTpMin = 99999999; - let chungchunTpInflMin = 99999999; + let chungchunInflTnMin = 99999999; + let chungchunInflTpMin = 99999999; + let chungchunTpInflMin = 99999999; - let chungchunEfflBodAvg = 0; - let chungchunEfflTnAvg = 0; - let chungchunEfflTpAvg = 0; - let chungchunEfflBodMax = 0; - let chungchunEfflTnMax = 0; - let chungchunEfflTpMax = 0; + let chungchunEfflBodAvg = 0, chungchunEfflTnAvg = 0, chungchunEfflTpAvg = 0; + let chungchunEfflBodMax = 0, chungchunEfflTnMax = 0, chungchunEfflTpMax = 0; let chungchunEfflBodMin = 99999999; - let chungchunEfflTnMin = 99999999; - let chungchunEfflTpMin = 99999999; - - // 청천시 - qltyChungchunData.forEach((item: any) => { - const month = item.msrDt.substring(4, 6); // 월 추출 - if(month === '01' || month === '02'){ - chungchunWinterDay += 1; - if(item.tpInflWqltTp != null && item.tpInflWqltTp != 0){ - noNullChungchunWinterDay+=1; - } - - const inflbod = nullToZero(item.inflAssctBfrWqltBod); - const infltn = nullToZero(item.inflAssctBfrWqltTn); - const infltp = nullToZero(item.inflAssctBfrWqltTp); - const effbod = nullToZero(item.efflWqltBod); - const efftn = nullToZero(item.efflWqltTn); - const efftp = nullToZero(item.efflWqltTp); - const tpInfl = nullToZero(item.tpInflWqltTp); - - chungchunInflBodAvg += inflbod; - chungchunInflTnAvg += infltn; - chungchunInflTpAvg += infltp; - chungchunEfflBodAvg += effbod; - chungchunEfflTnAvg += efftn; - chungchunEfflTpAvg += efftp; - chungchunTpInflAvg += tpInfl; - - if(chungchunInflBodMax < inflbod) chungchunInflBodMax = inflbod; - if(chungchunInflTnMax < infltn) chungchunInflTnMax = infltn; - if(chungchunInflTpMax < infltp) chungchunInflTpMax = infltp; - if(chungchunEfflBodMax < effbod) chungchunEfflBodMax = effbod; - if(chungchunEfflTnMax < efftn) chungchunEfflTnMax = efftn; - if(chungchunEfflTpMax < efftp) chungchunEfflTpMax = efftp; - if(chungchunTpInflMax < tpInfl) chungchunTpInflMax = tpInfl; - - if(chungchunInflBodMin > inflbod) chungchunInflBodMin = inflbod; - if(chungchunInflTnMin > infltn) chungchunInflTnMin = infltn; - if(chungchunInflTpMin > infltp) chungchunInflTpMin = infltp; - if(chungchunEfflBodMin > effbod) chungchunEfflBodMin = effbod; - if(chungchunEfflTnMin > efftn) chungchunEfflTnMin = efftn; - if(chungchunEfflTpMin > efftp) chungchunEfflTpMin = efftp; - if(chungchunTpInflMin > tpInfl) chungchunTpInflMin = tpInfl; - }else if(month === '06' || month === '07' || month === '08'){ - chungchunSummerDay += 1; - if(item.tpInflWqltTp != null && item.tpInflWqltTp != 0){ - noNullChungchunSummerDay+=1; - } - - chungchunInflBodAvgSummer += nullToZero(item.inflAssctBfrWqltBod); - chungchunInflTnAvgSummer += nullToZero(item.inflAssctBfrWqltTn); - chungchunInflTpAvgSummer += nullToZero(item.inflAssctBfrWqltTp); - chungchunEfflBodAvgSummer += nullToZero(item.efflWqltBod); - chungchunEfflTnAvgSummer += nullToZero(item.efflWqltTn); - chungchunEfflTpAvgSummer += nullToZero(item.efflWqltTp); - chungchunTpInflAvgSummer += nullToZero(item.tpInflWqltTp); - } - }); - - // 청천시 수질 데이터 수정 - // 하절기 - orgData[5].summerRain = qltyDataSetting(chungchunInflBodAvgSummer, chungchunSummerDay, 1); - orgData[6].summerRain = qltyDataSetting(chungchunEfflBodAvgSummer, chungchunSummerDay, 1); - orgData[7].summerRain = qltyDataSetting(chungchunInflTnAvgSummer, chungchunSummerDay, 3); - orgData[8].summerRain = qltyDataSetting(chungchunEfflTnAvgSummer, chungchunSummerDay, 3); - orgData[9].summerRain = qltyDataSetting(chungchunInflTpAvgSummer, chungchunSummerDay, 3); - orgData[10].summerRain = qltyDataSetting(chungchunEfflTpAvgSummer, chungchunSummerDay, 3); - - // 총인설비 - orgData[11].summerRain = qltyDataSetting(chungchunTpInflAvgSummer, noNullChungchunSummerDay, 3); - orgData[12].summerRain = qltyDataSetting(chungchunEfflTpAvgSummer, chungchunSummerDay, 3); - - // 동절기 - orgData[5].winterSunnyAvg = qltyDataSetting(chungchunInflBodAvg, chungchunWinterDay, 1); - orgData[6].winterSunnyAvg = qltyDataSetting(chungchunEfflBodAvg, chungchunWinterDay, 1); - orgData[7].winterSunnyAvg = qltyDataSetting(chungchunInflTnAvg, chungchunWinterDay, 3); - orgData[8].winterSunnyAvg = qltyDataSetting(chungchunEfflTnAvg, chungchunWinterDay, 3); - orgData[9].winterSunnyAvg = qltyDataSetting(chungchunInflTpAvg, chungchunWinterDay, 3); - orgData[10].winterSunnyAvg = qltyDataSetting(chungchunEfflTpAvg, chungchunWinterDay, 3); - - // 총인설비 - orgData[11].winterSunnyAvg = qltyDataSetting(chungchunTpInflAvg, noNullChungchunWinterDay, 3); - orgData[12].winterSunnyAvg = qltyDataSetting(chungchunInflTpAvg, chungchunWinterDay, 3); - - orgData[5].winterSunnyMax = roundUp(chungchunInflBodMax, 1).toFixed(1); - orgData[6].winterSunnyMax = roundUp(chungchunEfflBodMax, 1).toFixed(1); - orgData[7].winterSunnyMax = roundUp(chungchunInflTnMax, 3).toFixed(3); - orgData[8].winterSunnyMax = roundUp(chungchunEfflTnMax, 3).toFixed(3); - orgData[9].winterSunnyMax = roundUp(chungchunInflTpMax, 3).toFixed(3); - orgData[10].winterSunnyMax = roundUp(chungchunEfflTpMax, 3).toFixed(3); - orgData[11].winterSunnyMax = roundUp(chungchunTpInflMax, 3).toFixed(3); - orgData[12].winterSunnyMax = roundUp(chungchunInflTpMax, 3).toFixed(3); - - orgData[5].winterSunnyMin = chungchunInflBodMin === 99999999 ? '0.0' : roundUp(chungchunInflBodMin, 1).toFixed(1); - orgData[6].winterSunnyMin = chungchunEfflBodMin === 99999999 ? '0.0' : roundUp(chungchunEfflBodMin, 1).toFixed(1); - orgData[7].winterSunnyMin = chungchunInflTnMin === 99999999 ? '0.000' : roundUp(chungchunInflTnMin, 3).toFixed(3); - orgData[8].winterSunnyMin = chungchunEfflTnMin === 99999999 ? '0.000' : roundUp(chungchunEfflTnMin, 3).toFixed(3); - orgData[9].winterSunnyMin = chungchunInflTpMin === 99999999 ? '0.000' : roundUp(chungchunInflTpMin, 3).toFixed(3); - orgData[10].winterSunnyMin = chungchunEfflTpMin === 99999999 ? '0.000' : roundUp(chungchunEfflTpMin, 3).toFixed(3); - orgData[11].winterSunnyMin = chungchunTpInflMin === 99999999 ? '0.000' : roundUp(chungchunTpInflMin, 3).toFixed(3); - orgData[12].winterSunnyMin = chungchunInflTpMin === 99999999 ? '0.000' : roundUp(chungchunInflTpMin, 3).toFixed(3); + let chungchunEfflTnMin = 99999999; + let chungchunEfflTpMin = 99999999; // 강우시 변수 - let rainSummerDay = 0; - let noNullRainSummerDay = 0; + let rainSummerDay = 0, noNullRainSummerDay = 0; + let rainInflBodAvg = 0, rainInflTnAvg = 0, rainInflTpAvg = 0; + let rainEfflBodAvg = 0, rainEfflTnAvg = 0, rainEfflTpAvg = 0, rainTpInflAvg = 0; - let rainInflBodAvg = 0; - let rainInflTnAvg = 0; - let rainInflTpAvg = 0; - let rainEfflBodAvg = 0; - let rainEfflTnAvg = 0; - let rainEfflTpAvg = 0; - let rainTpInflAvg = 0; - - // 강우시 - qltyRainData.forEach((item: any) => { + let rainEffectDay = 0; + // 전체 대상 + const totalDay = qltyData.length; + let noNullYearDay = 0; + qltyData.forEach((item: any) => { + if (item.infoRnDay == null) item.infoRnDay = 0; const month = item.msrDt.substring(4, 6); // 월 추출 - if(month === '06' || month === '07' || month === '08'){ - rainSummerDay += 1; - if(item.tpInflWqltTp != null && item.tpInflWqltTp != 0){ - noNullRainSummerDay+=1; - } - - rainInflBodAvg += nullToZero(item.inflAssctBfrWqltBod); - rainInflTnAvg += nullToZero(item.inflAssctBfrWqltTn); - rainInflTpAvg += nullToZero(item.inflAssctBfrWqltTp); - rainEfflBodAvg += nullToZero(item.efflWqltBod); - rainEfflTnAvg += nullToZero(item.efflWqltTn); - rainEfflTpAvg += nullToZero(item.efflWqltTp); - rainTpInflAvg += nullToZero(item.tpInflWqltTp); + + if (item.infoRnDay >= 3) { + rainEffectDay = 2; + } else { + rainEffectDay--; } + + if (rainEffectDay >= 0) { + rainEffectDayArray.push(item.msrDt); + // 강우영향일 + if (month === '06' || month === '07' || month === '08') { + rainSummerDay += 1; + if (item.tpInflWqltTp != null && item.tpInflWqltTp != 0) { + noNullRainSummerDay += 1; + } + + rainInflBodAvg += nullToZero(item.inflAssctBfrWqltBod); + rainInflTnAvg += nullToZero(item.inflAssctBfrWqltTn); + rainInflTpAvg += nullToZero(item.inflAssctBfrWqltTp); + rainEfflBodAvg += nullToZero(item.efflWqltBod); + rainEfflTnAvg += nullToZero(item.efflWqltTn); + rainEfflTpAvg += nullToZero(item.efflWqltTp); + rainTpInflAvg += nullToZero(item.tpInflWqltTp); + } + } else if (rainEffectDay < 0) { //청천시 + + if (month === '01' || month === '02') { + chungchunWinterDay += 1; + if (item.tpInflWqltTp != null && item.tpInflWqltTp != 0) { + noNullChungchunWinterDay += 1; + } + + const inflbod = nullToZero(item.inflAssctBfrWqltBod); + const infltn = nullToZero(item.inflAssctBfrWqltTn); + const infltp = nullToZero(item.inflAssctBfrWqltTp); + const effbod = nullToZero(item.efflWqltBod); + const efftn = nullToZero(item.efflWqltTn); + const efftp = nullToZero(item.efflWqltTp); + const tpInfl = nullToZero(item.tpInflWqltTp); + + chungchunInflBodAvg += inflbod; + chungchunInflTnAvg += infltn; + chungchunInflTpAvg += infltp; + chungchunEfflBodAvg += effbod; + chungchunEfflTnAvg += efftn; + chungchunEfflTpAvg += efftp; + chungchunTpInflAvg += tpInfl; + + if (chungchunInflBodMax < inflbod) chungchunInflBodMax = inflbod; + if (chungchunInflTnMax < infltn) chungchunInflTnMax = infltn; + if (chungchunInflTpMax < infltp) chungchunInflTpMax = infltp; + if (chungchunEfflBodMax < effbod) chungchunEfflBodMax = effbod; + if (chungchunEfflTnMax < efftn) chungchunEfflTnMax = efftn; + if (chungchunEfflTpMax < efftp) chungchunEfflTpMax = efftp; + if (chungchunTpInflMax < tpInfl) chungchunTpInflMax = tpInfl; + + if (chungchunInflBodMin > inflbod) chungchunInflBodMin = inflbod; + if (chungchunInflTnMin > infltn) chungchunInflTnMin = infltn; + if (chungchunInflTpMin > infltp) chungchunInflTpMin = infltp; + if (chungchunEfflBodMin > effbod) chungchunEfflBodMin = effbod; + if (chungchunEfflTnMin > efftn) chungchunEfflTnMin = efftn; + if (chungchunEfflTpMin > efftp) chungchunEfflTpMin = efftp; + if (chungchunTpInflMin > tpInfl) chungchunTpInflMin = tpInfl; + } else if (month === '06' || month === '07' || month === '08') { + chungchunSummerDay += 1; + if (item.tpInflWqltTp != null && item.tpInflWqltTp != 0) { + noNullChungchunSummerDay += 1; + } + + chungchunInflBodAvgSummer += nullToZero(item.inflAssctBfrWqltBod); + chungchunInflTnAvgSummer += nullToZero(item.inflAssctBfrWqltTn); + chungchunInflTpAvgSummer += nullToZero(item.inflAssctBfrWqltTp); + chungchunEfflBodAvgSummer += nullToZero(item.efflWqltBod); + chungchunEfflTnAvgSummer += nullToZero(item.efflWqltTn); + chungchunEfflTpAvgSummer += nullToZero(item.efflWqltTp); + chungchunTpInflAvgSummer += nullToZero(item.tpInflWqltTp); + } + } + + + if (month === '01' || month === '02') { + winterDay += 1; + if (item.tpInflWqltTp != null && item.tpInflWqltTp != 0) { + noNullWinterDay += 1; + } + totalInflBodAvgWinter += nullToZero(item.inflAssctBfrWqltBod); + totalInflTnAvgWinter += nullToZero(item.inflAssctBfrWqltTn); + totalInflTpAvgWinter += nullToZero(item.inflAssctBfrWqltTp); + totalEfflBodAvgWinter += nullToZero(item.efflWqltBod); + totalEfflTnAvgWinter += nullToZero(item.efflWqltTn); + totalEfflTpAvgWinter += nullToZero(item.efflWqltTp); + totalTpInflAvgWinter += nullToZero(item.tpInflWqltTp); //총인 유입수질 TP(mg/L) + } else if (month === '06' || month === '07' || month === '08') { + summerDay += 1; + if (item.tpInflWqltTp != null && item.tpInflWqltTp != 0) { + noNullSummerDay += 1; + } + totalInflBodAvgSummer += nullToZero(item.inflAssctBfrWqltBod); + totalInflTnAvgSummer += nullToZero(item.inflAssctBfrWqltTn); + totalInflTpAvgSummer += nullToZero(item.inflAssctBfrWqltTp); + totalEfflBodAvgSummer += nullToZero(item.efflWqltBod); + totalEfflTnAvgSummer += nullToZero(item.efflWqltTn); + totalEfflTpAvgSummer += nullToZero(item.efflWqltTp); + totalTpInflAvgSummer += nullToZero(item.tpInflWqltTp); + } + + if (item.tpInflWqltTp != null && item.tpInflWqltTp != 0) { + noNullYearDay += 1; + } + operList.inflBod.yearTot += nullToZero(item.inflAssctBfrWqltBod); + operList.inflTN.yearTot += nullToZero(item.inflAssctBfrWqltTn); + operList.inflTP.yearTot += nullToZero(item.inflAssctBfrWqltTp); + operList.efflBod.yearTot += nullToZero(item.efflWqltBod); + operList.efflTN.yearTot += nullToZero(item.efflWqltTn); + operList.efflTP.yearTot += nullToZero(item.efflWqltTp); + operList.tpInflTp.yearTot += nullToZero(item.tpInflWqltTp); + // 최대 최소 + operList.inflBod.yearMax = Math.max(operList.inflBod.yearMax, nullToZero(item.inflAssctBfrWqltBod)); + operList.inflBod.yearMin = Math.min(operList.inflBod.yearMin, nullToZero(item.inflAssctBfrWqltBod)); + operList.inflTN.yearMax = Math.max(operList.inflTN.yearMax, nullToZero(item.inflAssctBfrWqltTn)); + operList.inflTN.yearMin = Math.min(operList.inflTN.yearMin, nullToZero(item.inflAssctBfrWqltTn)); + operList.inflTP.yearMax = Math.max(operList.inflTP.yearMax, nullToZero(item.inflAssctBfrWqltTp)); + operList.inflTP.yearMin = Math.min(operList.inflTP.yearMin, nullToZero(item.inflAssctBfrWqltTp)); + operList.efflBod.yearMax = Math.max(operList.efflBod.yearMax, nullToZero(item.efflWqltBod)); + operList.efflBod.yearMin = Math.min(operList.efflBod.yearMin, nullToZero(item.efflWqltBod)); + operList.efflTN.yearMax = Math.max(operList.efflTN.yearMax, nullToZero(item.efflWqltTn)); + operList.efflTN.yearMin = Math.min(operList.efflTN.yearMin, nullToZero(item.efflWqltTn)); + operList.efflTP.yearMax = Math.max(operList.efflTP.yearMax, nullToZero(item.efflWqltTp)); + operList.efflTP.yearMin = Math.min(operList.efflTP.yearMin, nullToZero(item.efflWqltTp)); + operList.tpInflTp.yearMax = Math.max(operList.tpInflTp.yearMax, nullToZero(item.tpInflWqltTp)); + operList.tpInflTp.yearMin = Math.min(operList.tpInflTp.yearMin, nullToZero(item.tpInflWqltTp)); + }); + operList.inflBod.yearAvg = operList.inflBod.yearTot / totalDay; + operList.inflTN.yearAvg = operList.inflTN.yearTot / totalDay; + operList.inflTP.yearAvg = operList.inflTP.yearTot / totalDay; + operList.efflBod.yearAvg = operList.efflBod.yearTot / totalDay; + operList.efflTN.yearAvg = operList.efflTN.yearTot / totalDay; + operList.efflTP.yearAvg = operList.efflTP.yearTot / totalDay; + operList.tpInflTp.yearAvg = operList.tpInflTp.yearTot / totalDay; + + orgData[5].yearAvg = qltyDataSetting(operList.inflBod.yearTot, totalDay, 1); // 연간 유입BOD + orgData[6].yearAvg = qltyDataSetting(operList.efflBod.yearTot, totalDay, 1); // 연간 방류BOD + orgData[7].yearAvg = qltyDataSetting(operList.inflTN.yearTot, totalDay, 3); // 연간 유입TN + orgData[8].yearAvg = qltyDataSetting(operList.efflTN.yearTot, totalDay, 3); // 연간 방류TN + orgData[9].yearAvg = qltyDataSetting(operList.inflTP.yearTot, totalDay, 3); // 연간 유입TP + orgData[10].yearAvg = qltyDataSetting(operList.efflTP.yearTot, totalDay, 3); // 연간 방류TP + //총인설비 + orgData[11].yearAvg = qltyDataSetting(operList.tpInflTp.yearTot, noNullYearDay, 3); // 연간 총인 유입수질 TP + orgData[12].yearAvg = qltyDataSetting(operList.efflTP.yearTot, totalDay, 3); // 연간 총인 유입수질 TP + + orgData[5].yearMax = roundUp(operList.inflBod.yearMax, 1); // 연간 유입BOD + orgData[6].yearMax = roundUp(operList.efflBod.yearMax, 1); // 연간 방류BOD + orgData[7].yearMax = roundUp(operList.inflTN.yearMax, 3).toFixed(3); // 연간 유입TN + orgData[8].yearMax = roundUp(operList.efflTN.yearMax, 3).toFixed(3); // 연간 방류TN + orgData[9].yearMax = roundUp(operList.inflTP.yearMax, 3).toFixed(3); // 연간 유입TP + orgData[10].yearMax = roundUp(operList.efflTP.yearMax, 3).toFixed(3); // 연간 방류TP + orgData[11].yearMax = roundUp(operList.tpInflTp.yearMax, 3).toFixed(3); // 연간 총인 유입수질 TP + orgData[12].yearMax = roundUp(operList.efflTP.yearMax, 3).toFixed(3); // 연간 총인 유입수질 TP + + orgData[5].yearMin = roundUp(operList.inflBod.yearMin, 1); // 연간 유입BOD + orgData[6].yearMin = roundUp(operList.efflBod.yearMin, 1); // 연간 방류BOD + orgData[7].yearMin = roundUp(operList.inflTN.yearMin, 3).toFixed(3); // 연간 유입TN + orgData[8].yearMin = roundUp(operList.efflTN.yearMin, 3).toFixed(3); // 연간 방류TN + orgData[9].yearMin = roundUp(operList.inflTP.yearMin, 3).toFixed(3); // 연간 유입TP + orgData[10].yearMin = roundUp(operList.efflTP.yearMin, 3).toFixed(3); // 연간 방류TP + orgData[11].yearMin = roundUp(operList.tpInflTp.yearMin, 3).toFixed(3); // 연간 총인 유입수질 TP + orgData[12].yearMin = roundUp(operList.efflTP.yearMin, 3).toFixed(3); // 연간 총인 유입수질 TP + + orgData[5].summerAll = qltyDataSetting(totalInflBodAvgSummer, summerDay, 1); // 하절기 전기간 유입BOD + orgData[6].summerAll = qltyDataSetting(totalEfflBodAvgSummer, summerDay, 1); // 하절기 전기간 방류BOD + orgData[7].summerAll = qltyDataSetting(totalInflTnAvgSummer, summerDay, 3); // 하절기 전기간 유입TN + orgData[8].summerAll = qltyDataSetting(totalEfflTnAvgSummer, summerDay, 3); // 하절기 전기간 방류TN + orgData[9].summerAll = qltyDataSetting(totalInflTpAvgSummer, summerDay, 3); // 하절기 전기간 유입TP + orgData[10].summerAll = qltyDataSetting(totalEfflTpAvgSummer, summerDay, 3); // 하절기 전기간 방류TP + //총인설비 + orgData[11].summerAll = qltyDataSetting(totalTpInflAvgSummer, noNullSummerDay, 3); + orgData[12].summerAll = qltyDataSetting(totalEfflTpAvgSummer, summerDay, 3); + // 강우시 수질 데이터 수정 orgData[5].summerThree = qltyDataSetting(rainInflBodAvg, rainSummerDay, 1); orgData[6].summerThree = qltyDataSetting(rainEfflBodAvg, rainSummerDay, 1); @@ -1010,10 +1011,61 @@ const calcWaterQltyData = async (data: any[], searchYear : string|undefined, fcl orgData[8].summerThree = qltyDataSetting(rainEfflTnAvg, rainSummerDay, 3); orgData[9].summerThree = qltyDataSetting(rainInflTpAvg, rainSummerDay, 3); orgData[10].summerThree = qltyDataSetting(rainEfflTpAvg, rainSummerDay, 3); - // 총인설비 orgData[11].summerThree = qltyDataSetting(rainTpInflAvg, noNullRainSummerDay, 3); - orgData[12].summerThree = qltyDataSetting(rainInflTpAvg, rainSummerDay, 3); + orgData[12].summerThree = qltyDataSetting(rainEfflTpAvg, rainSummerDay, 3); + + // 하절기 청천시 수질 데이터 + orgData[5].summerRain = qltyDataSetting(chungchunInflBodAvgSummer, chungchunSummerDay, 1); + orgData[6].summerRain = qltyDataSetting(chungchunEfflBodAvgSummer, chungchunSummerDay, 1); + orgData[7].summerRain = qltyDataSetting(chungchunInflTnAvgSummer, chungchunSummerDay, 3); + orgData[8].summerRain = qltyDataSetting(chungchunEfflTnAvgSummer, chungchunSummerDay, 3); + orgData[9].summerRain = qltyDataSetting(chungchunInflTpAvgSummer, chungchunSummerDay, 3); + orgData[10].summerRain = qltyDataSetting(chungchunEfflTpAvgSummer, chungchunSummerDay, 3); + // 총인설비 + orgData[11].summerRain = qltyDataSetting(chungchunTpInflAvgSummer, noNullChungchunSummerDay, 3); + orgData[12].summerRain = qltyDataSetting(chungchunEfflTpAvgSummer, chungchunSummerDay, 3); + + orgData[5].winterAll = qltyDataSetting(totalInflBodAvgWinter, winterDay, 1); // 동절기 전기간 유입BOD + orgData[6].winterAll = qltyDataSetting(totalEfflBodAvgWinter, winterDay, 1); // 동절기 전기간 방류BOD + orgData[7].winterAll = qltyDataSetting(totalInflTnAvgWinter, winterDay, 3); // 동절기 전기간 유입TN + orgData[8].winterAll = qltyDataSetting(totalEfflTnAvgWinter, winterDay, 3); // 동절기 전기간 방류TN + orgData[9].winterAll = qltyDataSetting(totalInflTpAvgWinter, winterDay, 3); // 동절기 전기간 유입TP + orgData[10].winterAll = qltyDataSetting(totalEfflTpAvgWinter, winterDay, 3); // 동절기 전기간 방류TP + //총인설비 + orgData[11].winterAll = qltyDataSetting(totalTpInflAvgWinter, noNullWinterDay, 3); + orgData[12].winterAll = qltyDataSetting(totalEfflTpAvgWinter, winterDay, 3); + + // 동절기 청천시 수질 데이터 + orgData[5].winterSunnyAvg = qltyDataSetting(chungchunInflBodAvg, chungchunWinterDay, 1); + orgData[6].winterSunnyAvg = qltyDataSetting(chungchunEfflBodAvg, chungchunWinterDay, 1); + orgData[7].winterSunnyAvg = qltyDataSetting(chungchunInflTnAvg, chungchunWinterDay, 3); + orgData[8].winterSunnyAvg = qltyDataSetting(chungchunEfflTnAvg, chungchunWinterDay, 3); + orgData[9].winterSunnyAvg = qltyDataSetting(chungchunInflTpAvg, chungchunWinterDay, 3); + orgData[10].winterSunnyAvg = qltyDataSetting(chungchunEfflTpAvg, chungchunWinterDay, 3); + // 총인설비 + orgData[11].winterSunnyAvg = qltyDataSetting(chungchunTpInflAvg, noNullChungchunWinterDay, 3); + orgData[12].winterSunnyAvg = qltyDataSetting(chungchunEfflTpAvg, chungchunWinterDay, 3); + + orgData[5].winterSunnyMax = roundUp(chungchunInflBodMax, 1).toFixed(1); + orgData[6].winterSunnyMax = roundUp(chungchunEfflBodMax, 1).toFixed(1); + orgData[7].winterSunnyMax = roundUp(chungchunInflTnMax, 3).toFixed(3); + orgData[8].winterSunnyMax = roundUp(chungchunEfflTnMax, 3).toFixed(3); + orgData[9].winterSunnyMax = roundUp(chungchunInflTpMax, 3).toFixed(3); + orgData[10].winterSunnyMax = roundUp(chungchunEfflTpMax, 3).toFixed(3); + orgData[11].winterSunnyMax = roundUp(chungchunTpInflMax, 3).toFixed(3); + orgData[12].winterSunnyMax = roundUp(chungchunEfflTpMax, 3).toFixed(3); + + orgData[5].winterSunnyMin = chungchunInflBodMin === 99999999 ? '0.0' : roundUp(chungchunInflBodMin, 1).toFixed(1); + orgData[6].winterSunnyMin = chungchunEfflBodMin === 99999999 ? '0.0' : roundUp(chungchunEfflBodMin, 1).toFixed(1); + orgData[7].winterSunnyMin = chungchunInflTnMin === 99999999 ? '0.000' : roundUp(chungchunInflTnMin, 3).toFixed(3); + orgData[8].winterSunnyMin = chungchunEfflTnMin === 99999999 ? '0.000' : roundUp(chungchunEfflTnMin, 3).toFixed(3); + orgData[9].winterSunnyMin = chungchunInflTpMin === 99999999 ? '0.000' : roundUp(chungchunInflTpMin, 3).toFixed(3); + orgData[10].winterSunnyMin = chungchunEfflTpMin === 99999999 ? '0.000' : roundUp(chungchunEfflTpMin, 3).toFixed(3); + orgData[11].winterSunnyMin = chungchunTpInflMin === 99999999 ? '0.000' : roundUp(chungchunTpInflMin, 3).toFixed(3); + orgData[12].winterSunnyMin = chungchunEfflTpMin === 99999999 ? '0.000' : roundUp(chungchunEfflTpMin, 3).toFixed(3); + + return orgData; } else { @@ -1039,256 +1091,257 @@ export type DashBoardActions = { export type DashBoardType = { DashboardMain: { isInit: boolean; - topLabelArray:string[]; + topLabelArray: string[]; }, - FcltyMain : { - isInit : boolean; - upHeadListFclty : TableUpperProps[]; - fcltyList : mainFcltyProps[]; + FcltyMain: { + isInit: boolean; + upHeadListFclty: TableUpperProps[]; + fcltyList: mainFcltyProps[]; }, - PlanFclty : { - isInit : boolean; - planYearList : string[]; - upHeadListPlanFclty : TableUpperProps[]; - planFcltyList : planFcltyListProps[]; - waterUsage1 : number | '', - waterUsage2 : number | '', - waterUsage3 : number | '', - isLoadingPlanFclty : boolean; + PlanFclty: { + isInit: boolean; + planYearList: string[]; + upHeadListPlanFclty: TableUpperProps[]; + planFcltyList: planFcltyListProps[]; + waterUsage1: number | '', + waterUsage2: number | '', + waterUsage3: number | '', + isLoadingPlanFclty: boolean; }, - OperationStatus : { - isInit : boolean; - upHeadListOperationStatus : TableUpperProps[]; - operationalStatusList : any; - isLoadingOperationStatus : boolean; + OperationStatus: { + isInit: boolean; + upHeadListOperationStatus: TableUpperProps[]; + operationalStatusList: any; + isLoadingOperationStatus: boolean; } - MainGraph : { - isInit : boolean; - chart_inflowRain : lineBarChartDataProps[]; - chart_bodRain : lineBarChartDataProps[]; - chart_inflTp : lineChartDataProps[]; - chart_chongInTp : lineChartDataProps[]; - chart_efflTp : lineChartDataProps[]; - chart_inflTn : lineChartDataProps[]; - chart_efflTn : lineChartDataProps[]; - chart_inflBod : lineChartDataProps[]; - chart_efflBod : lineChartDataProps[]; - waterQltyStandartd : any; - isLoadingMainGraph : boolean; + MainGraph: { + isInit: boolean; + chart_inflowRain: lineBarChartDataProps[]; + chart_bodRain: lineBarChartDataProps[]; + chart_inflTp: lineChartDataProps[]; + chart_chongInTp: lineChartDataProps[]; + chart_efflTp: lineChartDataProps[]; + chart_inflTn: lineChartDataProps[]; + chart_efflTn: lineChartDataProps[]; + chart_inflBod: lineChartDataProps[]; + chart_efflBod: lineChartDataProps[]; + waterQltyStandartd: any; + isLoadingMainGraph: boolean; }, - RainManage : { - isInit : boolean; - upHeadListRainManage : TableUpperProps[]; - rainManageList : RainManageListProps[]; - excelList : RainManageExcelType[]; - isLoadingRainManage : boolean; + RainManage: { + isInit: boolean; + upHeadListRainManage: TableUpperProps[]; + rainManageList: RainManageListProps[]; + excelList: RainManageExcelType[]; + isLoadingRainManage: boolean; + mainGraphData: any; + mainGraphYear: string; + mainGraphFcltyCd: string; + flowRateData: any; + flowRateYear: string; + flowRateFcltyCd: string; }, - - DrainageEquipment : { - isInit : boolean; - upHeadListDrainageEquipment : TableUpperProps[]; - drainageEquipmentList : DrainageEquipmentListProps[]; - drainageEquipmentExcelList : DrainageEquipmentExcelType[]; + + DrainageEquipment: { + isInit: boolean; + upHeadListDrainageEquipment: TableUpperProps[]; + drainageEquipmentList: DrainageEquipmentListProps[]; + drainageEquipmentExcelList: DrainageEquipmentExcelType[]; } } export type DashBoardStore = DashBoardType & DashBoardActions; -export const dashBoardInitState:DashBoardType = { +export const dashBoardInitState: DashBoardType = { DashboardMain: { isInit: false, topLabelArray: DashBoardTopLabelArray, }, - - FcltyMain : { - isInit : false, - upHeadListFclty : upHeadListFclty, - fcltyList : fcltyList, + + FcltyMain: { + isInit: false, + upHeadListFclty: upHeadListFclty, + fcltyList: fcltyList, }, - PlanFclty : { - isInit : true, - planYearList : planYearList, - upHeadListPlanFclty : upHeadListPlanFclty, - planFcltyList : planFcltyList, - waterUsage1 : 0, - waterUsage2 : 0, - waterUsage3 : 0, - isLoadingPlanFclty : false, + PlanFclty: { + isInit: true, + planYearList: planYearList, + upHeadListPlanFclty: upHeadListPlanFclty, + planFcltyList: planFcltyList, + waterUsage1: 0, + waterUsage2: 0, + waterUsage3: 0, + isLoadingPlanFclty: false, }, - OperationStatus : { - isInit : false, - upHeadListOperationStatus : upHeadListOperationStatus, - operationalStatusList : operationalStatusListOrg, - isLoadingOperationStatus : false, + OperationStatus: { + isInit: false, + upHeadListOperationStatus: upHeadListOperationStatus, + operationalStatusList: operationalStatusListOrg, + isLoadingOperationStatus: false, }, - MainGraph : { - isInit : false, - chart_inflowRain : chart_inflowRain, - chart_bodRain : chart_bodRain, - chart_inflTp : chart_inflTp, - chart_chongInTp : chart_chongInTp, - chart_efflTp : chart_efflTp, - chart_inflTn : chart_inflTn, - chart_efflTn : chart_efflTn, - chart_inflBod : chart_inflBod, - chart_efflBod : chart_efflBod, - waterQltyStandartd : waterQltyStandartd, - isLoadingMainGraph : false, + MainGraph: { + isInit: false, + chart_inflowRain: chart_inflowRain, + chart_bodRain: chart_bodRain, + chart_inflTp: chart_inflTp, + chart_chongInTp: chart_chongInTp, + chart_efflTp: chart_efflTp, + chart_inflTn: chart_inflTn, + chart_efflTn: chart_efflTn, + chart_inflBod: chart_inflBod, + chart_efflBod: chart_efflBod, + waterQltyStandartd: waterQltyStandartd, + isLoadingMainGraph: false, }, - RainManage : { - isInit : false, - upHeadListRainManage : upHeadListRainManage, - rainManageList : rainManageList, - excelList : [], - isLoadingRainManage : false, + RainManage: { + isInit: false, + upHeadListRainManage: upHeadListRainManage, + rainManageList: rainManageList, + excelList: [], + isLoadingRainManage: false, + mainGraphData: null, + mainGraphYear: '', + mainGraphFcltyCd: '', + flowRateData: null, + flowRateYear: '', + flowRateFcltyCd: '', }, - DrainageEquipment : { - isInit : false, - upHeadListDrainageEquipment : upHeadListDrainageEquipment, - drainageEquipmentList : drainageEquipmentList, - drainageEquipmentExcelList : [] + DrainageEquipment: { + isInit: false, + upHeadListDrainageEquipment: upHeadListDrainageEquipment, + drainageEquipmentList: drainageEquipmentList, + drainageEquipmentExcelList: [] } } /** * 데이터 폼 포맷설정 */ -export const dataFormSettingPlan=(item : planFcltyListProps, decmials : number) => { - DataForm({ type: 'float'+decmials, column: 'provisionalApproval', item: item as unknown as Record<string, unknown> }); - DataForm({ type: 'float'+decmials, column: 'now', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float'+decmials, column: 'stepOne', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float'+decmials, column: 'stepTwo', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float'+decmials, column: 'stepThree', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float'+decmials, column: 'stepFour', item: item as unknown as Record<string, unknown>}); +export const dataFormSettingPlan = (item: planFcltyListProps, decmials: number) => { + DataForm({ type: 'float' + decmials, column: 'provisionalApproval', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float' + decmials, column: 'now', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float' + decmials, column: 'stepOne', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float' + decmials, column: 'stepTwo', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float' + decmials, column: 'stepThree', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float' + decmials, column: 'stepFour', item: item as unknown as Record<string, unknown> }); } -export const dataFormSetting=(item : OperationalStatusListProps)=> { +export const dataFormSetting = (item: OperationalStatusListProps) => { DataForm({ type: 'float0', column: 'yearAvg', item: item as unknown as Record<string, unknown> }); - DataForm({ type: 'float0', column: 'yearMax', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float0', column: 'yearMin', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float0', column: 'summerAll', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float0', column: 'summerThree', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float0', column: 'summerRain', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float0', column: 'winterAll', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float0', column: 'winterSunnyAvg', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float0', column: 'winterSunnyMax', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float0', column: 'winterSunnyMin', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float0', column: 'groundWaterAvg', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float0', column: 'groundWaterSummer', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float0', column: 'groundWaterWinter', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float0', column: 'rainRate', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float0', column: 'rdi', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float0', column: 'yearMax', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float0', column: 'yearMin', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float0', column: 'summerAll', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float0', column: 'summerThree', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float0', column: 'summerRain', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float0', column: 'winterAll', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float0', column: 'winterSunnyAvg', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float0', column: 'winterSunnyMax', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float0', column: 'winterSunnyMin', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float0', column: 'groundWaterAvg', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float0', column: 'groundWaterSummer', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float0', column: 'groundWaterWinter', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float0', column: 'rainRate', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float0', column: 'rdi', item: item as unknown as Record<string, unknown> }); }; -export const dataFormSettingWqlty=(item : OperationalStatusListProps)=> { +export const dataFormSettingWqlty = (item: OperationalStatusListProps) => { DataForm({ type: 'float1', column: 'yearAvg', item: item as unknown as Record<string, unknown> }); - DataForm({ type: 'float1', column: 'yearMax', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float1', column: 'yearMin', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float1', column: 'summerAll', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float1', column: 'summerThree', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float1', column: 'summerRain', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float1', column: 'winterAll', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float1', column: 'winterSunnyAvg', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float1', column: 'winterSunnyMax', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float1', column: 'winterSunnyMin', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float1', column: 'groundWaterAvg', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float1', column: 'groundWaterSummer', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float1', column: 'groundWaterWinter', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float1', column: 'rainRate', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float1', column: 'rdi', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float1', column: 'yearMax', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float1', column: 'yearMin', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float1', column: 'summerAll', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float1', column: 'summerThree', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float1', column: 'summerRain', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float1', column: 'winterAll', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float1', column: 'winterSunnyAvg', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float1', column: 'winterSunnyMax', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float1', column: 'winterSunnyMin', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float1', column: 'groundWaterAvg', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float1', column: 'groundWaterSummer', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float1', column: 'groundWaterWinter', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float1', column: 'rainRate', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float1', column: 'rdi', item: item as unknown as Record<string, unknown> }); }; -const changeValue=(operationalStatus, data, keyTxt = 'INFL_AMNT')=>{ - operationalStatus.yearAvg = data.yearData ? (keyTxt.includes('_TP') || keyTxt.includes("_TN"))? data.yearData?Number(Math.round(data.yearData['AVG_'+keyTxt] * 1000) / 1000).toFixed(3).toString():'': data.yearData['AVG_'+keyTxt] : ''; - operationalStatus.yearMax = data.yearData ? (keyTxt.includes('_TP') || keyTxt.includes("_TN"))? data.yearData?Number(Math.round(data.yearData['MAX_'+keyTxt] * 1000) / 1000).toFixed(3).toString():'': data.yearData['MAX_'+keyTxt] : ''; - operationalStatus.yearMin = data.yearData ? (keyTxt.includes('_TP') || keyTxt.includes("_TN"))? data.yearData?Number(Math.round(data.yearData['MIN_'+keyTxt] * 1000) / 1000).toFixed(3).toString():'': data.yearData['MIN_'+keyTxt] : ''; - // 여름 - operationalStatus.summerAll = data.summer ? (keyTxt.includes('_TP') || keyTxt.includes("_TN"))? data.summer?Number(Math.round(data.summer['AVG_'+keyTxt] * 1000) / 1000).toFixed(3).toString():'': data.summer['AVG_'+keyTxt] : ''; - operationalStatus.summerThree = data.summerRainy ? (keyTxt.includes('_TP') || keyTxt.includes("_TN"))? data.summerRainy?Number(Math.round(data.summerRainy["AVG_" + keyTxt] * 1000) / 1000).toFixed(3).toString():'': data.summerRainy["AVG_" + keyTxt] : ''; - operationalStatus.summerRain = data.summerSunny ? (keyTxt.includes('_TP') || keyTxt.includes("_TN"))? data.summerSunny?Number(Math.round(data.summerSunny["AVG_" + keyTxt] * 1000) / 1000).toFixed(3).toString():'': data.summerSunny["AVG_" + keyTxt] : ''; - // 겨울 - operationalStatus.winterAll = data.winter ? (keyTxt.includes('_TP') || keyTxt.includes("_TN"))? data.winter?Number(Math.round(data.winter["AVG_" + keyTxt] * 1000) / 1000).toFixed(3).toString():'': data.winter["AVG_" + keyTxt] : ''; - operationalStatus.winterSunnyMin = data.winterSunny ? (keyTxt.includes('_TP') || keyTxt.includes("_TN"))? data.winterSunny?Number(Math.round(data.winterSunny["MIN_" + keyTxt] * 1000) / 1000).toFixed(3).toString():'': data.winterSunny["MIN_" + keyTxt] : ''; - operationalStatus.winterSunnyAvg = data.winterSunny ? (keyTxt.includes('_TP') || keyTxt.includes("_TN"))? data.winterSunny?Number(Math.round(data.winterSunny["AVG_" + keyTxt] * 1000) / 1000).toFixed(3).toString():'': data.winterSunny["AVG_" + keyTxt] : ''; - operationalStatus.winterSunnyMax = data.winterSunny ? (keyTxt.includes('_TP') || keyTxt.includes("_TN"))? data.winterSunny?Number(Math.round(data.winterSunny["MAX_" + keyTxt] * 1000) / 1000).toFixed(3).toString():'': data.winterSunny["MAX_" + keyTxt] : ''; - // 지하수 - - // 오염물질 - // RDII 추정 = 하절기(6~8월) 중 최저 유입 BOD일때 유입유량 x ((동절기 청천시 유입BOD - 하절기(6~8월) 중 최저 유입BOD) / (동절기 청천시 유입BOD)) - const summerLowFlow = data.summerBod && data.summerBod.INFL_AMNT && data.summerBod.INFL_AMNT != 'null' ? Number(data.summerBod.INFL_AMNT) : 0; - const winterAvgBod = data.winterSunny && data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD && data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD != 'null' ? Number(data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD) : 0; - const summerLowBod = data.summerBod && data.summerBod.MIN_INFL_ASSCT_BFR_WQLT_BOD && data.summerBod.MIN_INFL_ASSCT_BFR_WQLT_BOD != 'null' ? Number(data.summerBod.MIN_INFL_ASSCT_BFR_WQLT_BOD) : 0; - const summerLowRain = data.summerBod && data.summerBod.INFO_RN_DAY && data.summerBod.INFO_RN_DAY != 'null' ? Number(data.summerBod.INFO_RN_DAY) : 0; - operationalStatus.rdi = winterAvgBod === 0 ? 0 : roundUp(summerLowFlow * ((winterAvgBod - summerLowBod) / winterAvgBod), 1); - operationalStatus.rdiRain = summerLowRain; -} +// const changeValue = (operationalStatus, data, keyTxt = 'INFL_AMNT') => { +// //console.log('data.yearData', data.yearData); +// operationalStatus.yearAvg = data.yearData ? (keyTxt.includes('_TP') || keyTxt.includes("_TN")) ? data.yearData ? Number(Math.round(data.yearData['AVG_' + keyTxt] * 1000) / 1000).toFixed(3).toString() : '' : data.yearData['AVG_' + keyTxt] : ''; +// operationalStatus.yearMax = data.yearData ? (keyTxt.includes('_TP') || keyTxt.includes("_TN")) ? data.yearData ? Number(Math.round(data.yearData['MAX_' + keyTxt] * 1000) / 1000).toFixed(3).toString() : '' : data.yearData['MAX_' + keyTxt] : ''; +// operationalStatus.yearMin = data.yearData ? (keyTxt.includes('_TP') || keyTxt.includes("_TN")) ? data.yearData ? Number(Math.round(data.yearData['MIN_' + keyTxt] * 1000) / 1000).toFixed(3).toString() : '' : data.yearData['MIN_' + keyTxt] : ''; +// // 여름 +// operationalStatus.summerAll = data.summer ? (keyTxt.includes('_TP') || keyTxt.includes("_TN")) ? data.summer ? Number(Math.round(data.summer['AVG_' + keyTxt] * 1000) / 1000).toFixed(3).toString() : '' : data.summer['AVG_' + keyTxt] : ''; +// operationalStatus.summerThree = data.summerRainy ? (keyTxt.includes('_TP') || keyTxt.includes("_TN")) ? data.summerRainy ? Number(Math.round(data.summerRainy["AVG_" + keyTxt] * 1000) / 1000).toFixed(3).toString() : '' : data.summerRainy["AVG_" + keyTxt] : ''; +// operationalStatus.summerRain = data.summerSunny ? (keyTxt.includes('_TP') || keyTxt.includes("_TN")) ? data.summerSunny ? Number(Math.round(data.summerSunny["AVG_" + keyTxt] * 1000) / 1000).toFixed(3).toString() : '' : data.summerSunny["AVG_" + keyTxt] : ''; +// // 겨울 +// operationalStatus.winterAll = data.winter ? (keyTxt.includes('_TP') || keyTxt.includes("_TN")) ? data.winter ? Number(Math.round(data.winter["AVG_" + keyTxt] * 1000) / 1000).toFixed(3).toString() : '' : data.winter["AVG_" + keyTxt] : ''; +// operationalStatus.winterSunnyMin = data.winterSunny ? (keyTxt.includes('_TP') || keyTxt.includes("_TN")) ? data.winterSunny ? Number(Math.round(data.winterSunny["MIN_" + keyTxt] * 1000) / 1000).toFixed(3).toString() : '' : data.winterSunny["MIN_" + keyTxt] : ''; +// operationalStatus.winterSunnyAvg = data.winterSunny ? (keyTxt.includes('_TP') || keyTxt.includes("_TN")) ? data.winterSunny ? Number(Math.round(data.winterSunny["AVG_" + keyTxt] * 1000) / 1000).toFixed(3).toString() : '' : data.winterSunny["AVG_" + keyTxt] : ''; +// operationalStatus.winterSunnyMax = data.winterSunny ? (keyTxt.includes('_TP') || keyTxt.includes("_TN")) ? data.winterSunny ? Number(Math.round(data.winterSunny["MAX_" + keyTxt] * 1000) / 1000).toFixed(3).toString() : '' : data.winterSunny["MAX_" + keyTxt] : ''; +// // 지하수 -const calcValue = (operationalStatus, data, keyTxt='INFL_ASSCT_BFR_WQLT_TP') => { - // 연간 - operationalStatus.yearAvg = data.yearData && data.yearData['AVG_'+keyTxt] ? Number(Math.round(data.yearData['AVG_'+keyTxt] * 1000) / 1000).toFixed(3).toString() : ''; - operationalStatus.yearMax = data.yearData && data.yearData['MAX_'+keyTxt] ? Number(Math.round(data.yearData['MAX_'+keyTxt] * 1000) / 1000).toFixed(3).toString() : ''; - operationalStatus.yearMin = data.yearData && data.yearData['MIN_'+keyTxt] ? Number(Math.round(data.yearData['MIN_'+keyTxt] * 1000) / 1000).toFixed(3).toString() : ''; - // 여름 - operationalStatus.summerAll = data.summer && data.summer['AVG_'+keyTxt] ? Number(Math.round(data.summer['AVG_'+keyTxt] * 0.8 * 1000) / 1000).toFixed(3).toString() : ''; - operationalStatus.summerThree = data.summerRainy && data.summerRainy['AVG_'+keyTxt] ? Number(Math.round(data.summerRainy['AVG_'+keyTxt] * 0.82 * 1000) / 1000).toFixed(3).toString() : ''; - operationalStatus.summerRain = data.summerSunny && data.summerSunny['AVG_'+keyTxt] ? Number(Math.round(data.summerSunny['AVG_'+keyTxt] * 0.74 * 1000) / 1000).toFixed(3).toString() : ''; - // 겨울 - operationalStatus.winterAll = data.winter && data.winter['AVG_'+keyTxt] ? Number(Math.round(data.winter['AVG_'+keyTxt] * 1.16*1000) / 1000).toFixed(3).toString() : ''; - operationalStatus.winterSunnyMin = data.winterSunny && data.winterSunny['MIN_'+keyTxt] ? Number(Math.round(data.winterSunny['MIN_'+keyTxt] * 1.2 * 1000) / 1000).toFixed(3).toString() : ''; - operationalStatus.winterSunnyAvg = data.winterSunny && data.winterSunny['AVG_'+keyTxt] ? Number(Math.round(data.winterSunny['AVG_'+keyTxt] * 1.25 *1000) / 1000).toFixed(3).toString() : ''; - operationalStatus.winterSunnyMax = data.winterSunny && data.winterSunny['MAX_'+keyTxt] ? Number(Math.round(data.winterSunny['MAX_'+keyTxt] * 1.3 *1000) / 1000).toFixed(3).toString() : ''; -} +// // 오염물질 +// // RDII 추정 = 하절기(6~8월) 중 최저 유입 BOD일때 유입유량 x ((동절기 청천시 유입BOD - 하절기(6~8월) 중 최저 유입BOD) / (동절기 청천시 유입BOD)) +// const summerLowFlow = data.summerBod && data.summerBod.INFL_AMNT && data.summerBod.INFL_AMNT != 'null' ? Number(data.summerBod.INFL_AMNT) : 0; +// const winterAvgBod = data.winterSunny && data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD && data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD != 'null' ? Number(data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD) : 0; +// const summerLowBod = data.summerBod && data.summerBod.MIN_INFL_ASSCT_BFR_WQLT_BOD && data.summerBod.MIN_INFL_ASSCT_BFR_WQLT_BOD != 'null' ? Number(data.summerBod.MIN_INFL_ASSCT_BFR_WQLT_BOD) : 0; +// const summerLowRain = data.summerBod && data.summerBod.INFO_RN_DAY && data.summerBod.INFO_RN_DAY != 'null' ? Number(data.summerBod.INFO_RN_DAY) : 0; +// operationalStatus.rdi = winterAvgBod === 0 ? 0 : roundUp(summerLowFlow * ((winterAvgBod - summerLowBod) / winterAvgBod), 1); +// operationalStatus.rdiRain = summerLowRain; +// } -type RtnFlowType = { - inflAmnt: string | number; - infoRnDay: string | number; - bscFctCd: string; - msrDt: string; -} +// const calcValue = (operationalStatus, data, keyTxt = 'INFL_ASSCT_BFR_WQLT_TP') => { +// // 연간 +// operationalStatus.yearAvg = data.yearData && data.yearData['AVG_' + keyTxt] ? Number(Math.round(data.yearData['AVG_' + keyTxt] * 1000) / 1000).toFixed(3).toString() : ''; +// operationalStatus.yearMax = data.yearData && data.yearData['MAX_' + keyTxt] ? Number(Math.round(data.yearData['MAX_' + keyTxt] * 1000) / 1000).toFixed(3).toString() : ''; +// operationalStatus.yearMin = data.yearData && data.yearData['MIN_' + keyTxt] ? Number(Math.round(data.yearData['MIN_' + keyTxt] * 1000) / 1000).toFixed(3).toString() : ''; +// // 여름 +// operationalStatus.summerAll = data.summer && data.summer['AVG_' + keyTxt] ? Number(Math.round(data.summer['AVG_' + keyTxt] * 0.8 * 1000) / 1000).toFixed(3).toString() : ''; +// operationalStatus.summerThree = data.summerRainy && data.summerRainy['AVG_' + keyTxt] ? Number(Math.round(data.summerRainy['AVG_' + keyTxt] * 0.82 * 1000) / 1000).toFixed(3).toString() : ''; +// operationalStatus.summerRain = data.summerSunny && data.summerSunny['AVG_' + keyTxt] ? Number(Math.round(data.summerSunny['AVG_' + keyTxt] * 0.74 * 1000) / 1000).toFixed(3).toString() : ''; +// // 겨울 +// operationalStatus.winterAll = data.winter && data.winter['AVG_' + keyTxt] ? Number(Math.round(data.winter['AVG_' + keyTxt] * 1.16 * 1000) / 1000).toFixed(3).toString() : ''; +// operationalStatus.winterSunnyMin = data.winterSunny && data.winterSunny['MIN_' + keyTxt] ? Number(Math.round(data.winterSunny['MIN_' + keyTxt] * 1.2 * 1000) / 1000).toFixed(3).toString() : ''; +// operationalStatus.winterSunnyAvg = data.winterSunny && data.winterSunny['AVG_' + keyTxt] ? Number(Math.round(data.winterSunny['AVG_' + keyTxt] * 1.25 * 1000) / 1000).toFixed(3).toString() : ''; +// operationalStatus.winterSunnyMax = data.winterSunny && data.winterSunny['MAX_' + keyTxt] ? Number(Math.round(data.winterSunny['MAX_' + keyTxt] * 1.3 * 1000) / 1000).toFixed(3).toString() : ''; +// } -type RtnFlowResponse = { - data: RtnFlowType[]; +type wtrUsgYearsType = { + YEAR: string; + WTR_USG: number; +} +type wtrUsgYearsResponse = { + data: wtrUsgYearsType[]; count: number; } const searchWaterUsage = async (searchYear: string, fcltyCd: string) => { - const waterUsage = [0, 0, 0]; - - for (let i = 0; i < 3; i++) { - try { - const res = await getFetch('fo', '/fclt/flow/' + fcltyCd + '/' + (Number(searchYear)-i)) as RtnFlowResponse; - let totWaterUsage = 0; - - if (res && res.data && Array.isArray(res.data)) { - res.data.forEach((item: RtnFlowType) => { - totWaterUsage += Number(item.inflAmnt); - }); - - if (res.count > 0) { - const avg = totWaterUsage / res.count; - waterUsage[i] = parseFloat((avg * 0.65).toFixed(2)); - } - } - } catch (error) { - console.error(`Error fetching water usage for year ${Number(searchYear)-i}:`, error); - waterUsage[i] = 0; - } + const waterUsage = [0, 0, 0]; // searchYear, searchYear-1, searchYear-2 + + const res = await getFetch('fo', '/dashBoard/wtrUsgYears/' + searchYear + '/' + fcltyCd) as wtrUsgYearsResponse; + let i = 0; + if (res && res.data && Array.isArray(res.data)) { + res.data.forEach((item: wtrUsgYearsType) => { + waterUsage[i] = item.WTR_USG; + i++; + }); } - + return waterUsage; } -export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) => void)=>DashBoardActions=(set: (fn: (state: DashBoardType) => DashBoardType) => void) => { +export const dashBoardReducer = ( + set: (fn: (state: DashBoardType) => DashBoardType) => void, + get: () => DashBoardType +): DashBoardActions => { return { initializeDashBoardData: async () => { set((state: DashBoardType) => ({ @@ -1301,7 +1354,7 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) }, /** 시설정보 및 운영현황 - 계획정보 */ - searchPlanInfo : async (targetYear: string, fcltyCd: string) => { + searchPlanInfo: async (targetYear: string, fcltyCd: string) => { set((state: DashBoardType) => ({ ...state, PlanFclty: { @@ -1310,71 +1363,70 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) }, })); let waterUsage = [0, 0, 0]; - waterUsage = await searchWaterUsage(targetYear, fcltyCd); - + waterUsage = await searchWaterUsage(targetYear, fcltyCd); // /fclt/flow/{fcltyCd}/{year} + set((state: DashBoardType) => ({ ...state, - PlanFclty : { + PlanFclty: { ...state.PlanFclty, - waterUsage1 : waterUsage[0] || 0, - waterUsage2 : waterUsage[1] || 0, - waterUsage3 : waterUsage[2] || 0, + waterUsage1: waterUsage[0] || 0, + waterUsage2: waterUsage[1] || 0, + waterUsage3: waterUsage[2] || 0, }, })); - const res = await fetchFacilityPlan(targetYear, fcltyCd, set); - - let minDiff=9999999; + const res = await fetchFacilityPlan(targetYear, fcltyCd, set); // /dc-service/api/v1/nowDmgMtncGnsvy + let minDiff = 9999999; let minIndex; - res.forEach((el:any, idx) => { - if(minDiff>Math.abs(Number(targetYear) - Number(el.planYr))){ - minDiff=Math.abs(Number(targetYear) - Number(el.planYr)); - minIndex=idx; + res.forEach((el: any, idx) => { + if (minDiff > Math.abs(Number(targetYear) - Number(el.planYr))) { + minDiff = Math.abs(Number(targetYear) - Number(el.planYr)); + minIndex = idx; } }); const rtnPlanData = res[minIndex]; planFcltyList.map((el, index) => { - planYearList = []; - planYearList.push(rtnPlanData?rtnPlanData.planYrDt4.toString():""); - planYearList.push(rtnPlanData?rtnPlanData.planYr.toString():""); - planYearList.push(rtnPlanData?rtnPlanData.planYrDc1.toString():""); - planYearList.push(rtnPlanData?rtnPlanData.planYrDc2.toString():""); - planYearList.push(rtnPlanData?rtnPlanData.planYrDc3.toString():""); - planYearList.push(rtnPlanData?rtnPlanData.planYrDc4.toString():""); + planYearList = []; + planYearList.push(rtnPlanData ? rtnPlanData.planYrDt4.toString() : ""); + planYearList.push(rtnPlanData ? rtnPlanData.planYr.toString() : ""); + planYearList.push(rtnPlanData ? rtnPlanData.planYrDc1.toString() : ""); + planYearList.push(rtnPlanData ? rtnPlanData.planYrDc2.toString() : ""); + planYearList.push(rtnPlanData ? rtnPlanData.planYrDc3.toString() : ""); + planYearList.push(rtnPlanData ? rtnPlanData.planYrDc4.toString() : ""); - if(index === 0){ // 증설계획(㎥/일) - el.provisionalApproval = rtnPlanData?rtnPlanData.planSwageQntSumDt4:''; // 기승인4단계 - el.now = rtnPlanData?rtnPlanData.planSwageQntSum:''; // 현재 - el.stepOne = rtnPlanData?rtnPlanData.planSwageQntSumDc1:''; // 1단계 - el.stepTwo = rtnPlanData?rtnPlanData.planSwageQntSumDc2:''; // 2단계 - el.stepThree = rtnPlanData?rtnPlanData.planSwageQntSumDc3:''; // 3단계 - el.stepFour = rtnPlanData?rtnPlanData.planSwageQntSumDc4:''; // 4단계 + if (index === 0) { // 증설계획(㎥/일) + el.provisionalApproval = rtnPlanData ? rtnPlanData.planSwageQntSumDt4 : ''; // 기승인4단계 + el.now = rtnPlanData ? rtnPlanData.planSwageQntSum : ''; // 현재 + el.stepOne = rtnPlanData ? rtnPlanData.planSwageQntSumDc1 : ''; // 1단계 + el.stepTwo = rtnPlanData ? rtnPlanData.planSwageQntSumDc2 : ''; // 2단계 + el.stepThree = rtnPlanData ? rtnPlanData.planSwageQntSumDc3 : ''; // 3단계 + el.stepFour = rtnPlanData ? rtnPlanData.planSwageQntSumDc4 : ''; // 4단계 } - if(index === 1){ // 처리인구(인) - el.provisionalApproval = rtnPlanData?rtnPlanData.swtrmtPpltnCntDt4:''; - el.now = rtnPlanData?rtnPlanData.swtrmtPpltnCnt:''; - el.stepOne = rtnPlanData?rtnPlanData.swtrmtPpltnCntDc1:''; - el.stepTwo = rtnPlanData?rtnPlanData.swtrmtPpltnCntDc2:''; - el.stepThree = rtnPlanData?rtnPlanData.swtrmtPpltnCntDc3:''; - el.stepFour = rtnPlanData?rtnPlanData.swtrmtPpltnCntDc4:''; + if (index === 1) { // 처리인구(인) + el.provisionalApproval = rtnPlanData ? rtnPlanData.swtrmtPpltnCntDt4 : ''; + el.now = rtnPlanData ? rtnPlanData.swtrmtPpltnCnt : ''; + el.stepOne = rtnPlanData ? rtnPlanData.swtrmtPpltnCntDc1 : ''; + el.stepTwo = rtnPlanData ? rtnPlanData.swtrmtPpltnCntDc2 : ''; + el.stepThree = rtnPlanData ? rtnPlanData.swtrmtPpltnCntDc3 : ''; + el.stepFour = rtnPlanData ? rtnPlanData.swtrmtPpltnCntDc4 : ''; } - if(index === 2){ // 처리구역(km²) - el.provisionalApproval = rtnPlanData?rtnPlanData.swtrmtZoneAreaDt4:''; - el.now = rtnPlanData?rtnPlanData.swtrmtZoneArea:''; - el.stepOne = rtnPlanData?rtnPlanData.swtrmtZoneAreaDc1:''; - el.stepTwo = rtnPlanData?rtnPlanData.swtrmtZoneAreaDc2:''; - el.stepThree = rtnPlanData?rtnPlanData.swtrmtZoneAreaDc3:''; - el.stepFour = rtnPlanData?rtnPlanData.swtrmtZoneAreaDc4:''; + if (index === 2) { // 처리구역(km²) + el.provisionalApproval = rtnPlanData ? rtnPlanData.swtrmtZoneAreaDt4 : ''; + el.now = rtnPlanData ? rtnPlanData.swtrmtZoneArea : ''; + el.stepOne = rtnPlanData ? rtnPlanData.swtrmtZoneAreaDc1 : ''; + el.stepTwo = rtnPlanData ? rtnPlanData.swtrmtZoneAreaDc2 : ''; + el.stepThree = rtnPlanData ? rtnPlanData.swtrmtZoneAreaDc3 : ''; + el.stepFour = rtnPlanData ? rtnPlanData.swtrmtZoneAreaDc4 : ''; } - if(index === 4){ // 물사용량 - el.provisionalApproval = rtnPlanData?rtnPlanData.ugwtUseSum:''; + if (index === 4) { // 물사용량 + el.provisionalApproval = rtnPlanData ? rtnPlanData.ugwtUseSum : ''; } - - if(index === 2){ // 처리구역(km²) - dataFormSettingPlan(el, 1); - }else if(index == 4){ // 물사용량 - }else{ + + if (index === 2) { // 처리구역(km²) + dataFormSettingPlan(el, 1); + } else if (index == 4) { // 물사용량 + } else { dataFormSettingPlan(el, 0); } @@ -1392,6 +1444,26 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) /** 시설정보 및 운영현황 - 운영실태 */ searchOperationStatus: async (isExcel: boolean, searchYear: string, fcltyCd: string) => { + const state = get(); + let mainGraphData: ApiRes = { data: [], count: 0 }; + + // 이미 조회된 데이터가 있는지 확인 (연도와 시설코드가 같을 때만) + if (state.RainManage.mainGraphYear === searchYear && state.RainManage.mainGraphFcltyCd === fcltyCd && state.RainManage.mainGraphData) { + mainGraphData = state.RainManage.mainGraphData; + } else { + mainGraphData = await fetchMainGraph(searchYear, fcltyCd); + // 조회된 데이터 저장 + set((state: DashBoardType) => ({ + ...state, + RainManage: { + ...state.RainManage, + mainGraphData: mainGraphData, + mainGraphYear: searchYear, + mainGraphFcltyCd: fcltyCd, + } + })); + } + set((state: DashBoardType) => ({ ...state, OperationStatus: { @@ -1399,498 +1471,345 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) isLoadingOperationStatus: true, }, })); - const res = await fetchOperationStatus(searchYear, fcltyCd); // /dashBoard/mngInfo/ - const data = res.data; - let yearMaxSumFlow = 1; // 연간운영실태 최대 - let yearMinSumFlow = 1; // 연간운영실태 최소 - let yearAvgSumFlow = 1; // 연간운영실태 평균 - let summerAll = 1; // 하절기 전기간 - let summerThree = 1; // 하절기 - let summerRain = 1; // 하절기 - let winterAll = 1; // 동절기 전기간 - let winterSunnyMin = 1; // 동절기 청천 최소 - let winterSunnyAvg = 1; // 동절기 청천 평균 - let winterSunnyMax = 1; // 동절기 청천 최대 - - // 최대, 최소 총 유입량 (1년 하루 합계의 최대, 최소) + // const fcltCdList = [fcltyCd]; + //const wtrUsgRes: any = await fetchWtrUsgYmList(searchYear, fcltCdList); // 수용가의 실제 물사용량 - // 하절기 청천시, 강우시 재계산 리턴값 - let summerChungchunFlow = 1; // 하절기 청천 - let summerChungchunReturnAmnt = 1; // 하절기 청천 - let summerChungchunEfflAmnt = 1; // 하절기 청천 - let summerRainInflAmnt = 1; // 하절기 강우시 - let summerRainReturnAmnt = 1; // 하절기 강우시 - let summerRainEfflAmnt = 1; // 하절기 강우시 - - // 동절기 청천시 최대, 최소, 평균 리턴값 - let winterChungchunFlowAvg = 1; // 동절기 청천 하수유입량 평균 - let winterChungchunFlowMax = 1; // 동절기 청천 하수유입량 최대 - let winterChungchunFlowMin = 1; // 동절기 청천 하수유입량 최소 - let winterChungchunReturnAvg = 1; // 동절기 청천 총인반류수 평균 - let winterChungchunReturnMax = 1; // 동절기 청천 총인반류수 최대 - let winterChungchunReturnMin = 1; // 동절기 청천 총인반류수 최소 - let winterChungchunEfflAvg = 1; // 동절기 청천 방류량 평균 - let winterChungchunEfflMax = 1; // 동절기 청천 방류량 최대 - let winterChungchunEfflMin = 1; // 동절기 청천 방류량 최소 - + const operList = { + inFlow: { + yearTot: 0, yearAvg: 0, yearMax: 0, yearMin: 9999999 + , summerTot: 0, summerAvg: 0, summerRainy: 0, summerSunny: 0 + , winterTot: 0, winterAvg: 0, winterSunny: 0, winterSunnyMax: 0, winterSunnyMin: 9999999 + , groundTot: 0, groundSummer: 0, groundWinter: 0, rdii: 0, rdiiRainAmt: 0 + }, + inflAmt: { + yearTot: 0, yearAvg: 0, yearMax: 0, yearMin: 9999999, yearSunny: 0 + , summerTot: 0, summerAvg: 0, summerRainy: 0, summerSunny: 0 + , winterTot: 0, winterAvg: 0, winterSunny: 0, winterSunnyMax: 0, winterSunnyMin: 9999999 + , groundTot: 0, groundSummer: 0, groundWinter: 0, rdii: 0, rdiiRainAmt: 0 + }, + sumFlow: { + yearTot: 0, yearAvg: 0, yearMax: 0, yearMin: 9999999 + , summerTot: 0, summerAvg: 0, summerRainy: 0, summerSunny: 0 + , winterTot: 0, winterAvg: 0, winterSunny: 0, winterSunnyMax: 0, winterSunnyMin: 9999999 + , groundTot: 0, groundSummer: 0, groundWinter: 0, rdii: 0, rdiiRainAmt: 0 + }, + returnFlow: { + yearTot: 0, yearAvg: 0, yearMax: 0, yearMin: 9999999 + , summerTot: 0, summerAvg: 0, summerRainy: 0, summerSunny: 0 + , winterTot: 0, winterAvg: 0, winterSunny: 0, winterSunnyMax: 0, winterSunnyMin: 9999999 + , groundTot: 0, groundSummer: 0, groundWinter: 0, rdii: 0, rdiiRainAmt: 0 + }, + efflFlow: { + yearTot: 0, yearAvg: 0, yearMax: 0, yearMin: 9999999 + , summerTot: 0, summerAvg: 0, summerRainy: 0, summerSunny: 0 + , winterTot: 0, winterAvg: 0, winterSunny: 0, winterSunnyMax: 0, winterSunnyMin: 9999999 + , groundTot: 0, groundSummer: 0, groundWinter: 0, rdii: 0, rdiiRainAmt: 0 + }, + }; const clone = (arr: any[]) => arr?.map(v => ({ ...v })) ?? []; - let rdiPercent = 0; + let rdiPercent = 0; let operationalStatusList = clone(operationalStatusListOrg); - if(data && operationalStatusList){ + if (operationalStatusList) { // 유량 - 유입 if (operationalStatusList?.[0]) { - // 연간 - const yearAvg = data.yearData===null ? '' : ( - (data.yearData.AVG_INFL_AMNT?data.yearData.AVG_INFL_AMNT:0) + // 실제유입량(반류수제거)(m³/일) 평균 - (data.yearData.AVG_SUM_FLOW?data.yearData.AVG_SUM_FLOW:0) + // api 주석처리되어 있음. - (data.yearData.AVG_RETURN_AMNT?data.yearData.AVG_RETURN_AMNT:0) // 반류수유량(m³/일) 평균 - ); - const yearMax = data.yearData===null ? '' : ( - (data.yearData.MAX_INFL_AMNT?data.yearData.MAX_INFL_AMNT:0) + // 실제유입량(반류수제거)(m³/일) 최대 - (data.yearData.MAX_SUM_FLOW?data.yearData.MAX_SUM_FLOW:0) + // api 주석처리되어 있음. - (data.yearData.MAX_RETURN_AMNT?data.yearData.MAX_RETURN_AMNT:0) // 반류수유량(m³/일) 최대 - ); - const yearMin = data.yearData===null ? '' : ( - (data.yearData.MIN_INFL_AMNT?data.yearData.MIN_INFL_AMNT:0) + // 실제유입량(반류수제거)(m³/일) 최소 - (data.yearData.MIN_SUM_FLOW?data.yearData.MIN_SUM_FLOW:0) + // api 주석처리되어 있음. - (data.yearData.MIN_RETURN_AMNT?data.yearData.MIN_RETURN_AMNT:0) // 반류수유량(m³/일) 최소 - ); - operationalStatusList[0].yearAvg = yearAvg; - operationalStatusList[0].yearMax = yearMax; - operationalStatusList[0].yearMin = yearMin; - // 여름 - operationalStatusList[0].summerAll = data.summer === null ? '' : ( - (data.summer.AVG_INFL_AMNT ? data.summer.AVG_INFL_AMNT : 0) + - (data.summer.AVG_SUM_FLOW ? data.summer.AVG_SUM_FLOW : 0) - ); - operationalStatusList[0].summerThree = data.summerRainy === null ? '' : ( // 하절기 강우시 - (data.summerRainy.AVG_INFL_AMNT ? data.summerRainy.AVG_INFL_AMNT : 0) + - (data.summerRainy.AVG_SUM_FLOW ? data.summerRainy.AVG_SUM_FLOW : 0) - ); - operationalStatusList[0].summerRain = data.summerSunny === null ? '' : ( // 하절기 청천시 - (data.summerSunny.AVG_INFL_AMNT ? data.summerSunny.AVG_INFL_AMNT : 0) + - (data.summerSunny.AVG_SUM_FLOW ? data.summerSunny.AVG_SUM_FLOW : 0) - ); - // 겨울 - operationalStatusList[0].winterAll = data.winter === null ? '' : ( // 동절기 전기간 - (data.winter.AVG_INFL_AMNT ? data.winter.AVG_INFL_AMNT : 0) + - (data.winter.AVG_SUM_FLOW ? data.winter.AVG_SUM_FLOW : 0) + - (data.winter.AVG_TP_INFL_WQLT_TP ? data.winter.AVG_TP_INFL_WQLT_TP : 0) - ); - operationalStatusList[0].winterSunnyMin = data.winterSunny === null ? '' : (// 동절기 청천 최소 - (data.winterSunny.MIN_INFL_AMNT ? data.winterSunny.MIN_INFL_AMNT : 0) + - (data.winterSunny.MIN_SUM_FLOW ? data.winterSunny.MIN_SUM_FLOW : 0) + - (data.winterSunny.MIN_TP_INFL_WQLT_TP ? data.winterSunny.MIN_TP_INFL_WQLT_TP : 0) - ); - operationalStatusList[0].winterSunnyAvg = data.winterSunny === null ? '' : (// 동절기 청천 평균 - (data.winterSunny.AVG_INFL_AMNT ? data.winterSunny.AVG_INFL_AMNT : 0) + - (data.winterSunny.AVG_SUM_FLOW ? data.winterSunny.AVG_SUM_FLOW : 0) + - (data.winterSunny.AVG_TP_INFL_WQLT_TP ? data.winterSunny.AVG_TP_INFL_WQLT_TP : 0) - ); - operationalStatusList[0].winterSunnyMax = data.winterSunny === null ? '' : (// 동절기 청천 최대 - (data.winterSunny.MAX_INFL_AMNT ? data.winterSunny.MAX_INFL_AMNT : 0) + - (data.winterSunny.MAX_SUM_FLOW ? data.winterSunny.MAX_SUM_FLOW : 0) + - (data.winterSunny.MAX_TP_INFL_WQLT_TP ? data.winterSunny.MAX_TP_INFL_WQLT_TP : 0) - ); // 연계처리수 - let yearDay = 0; - let summerDay = 0; - let winterDay = 0; - let summerRainDay = 0; - let summerChungchunDay = 0; - let winterChungchuDay = 0; + let yearDay = 0, summerDay = 0, winterDay = 0; // 연간 일수, 하절기 일수, 동절기 일수 + let summerRainDay = 0, summerChungchunDay = 0, winterChungchuDay = 0; // 하절기 강우 일수, 하절기 청천 일수, 동절기 청천 일수 - let totFlowMax = 0; - let totFlowMin = 9999999; - - let yearSumFlow = 0; - let maxYearSumFlow = 0; - let minYearSumFlow = 9999999; - let summerSumFlow = 0; - let summerChungchunSumFlow = 0; - let summerRainDaySumFlow = 0; - let summerRainDayFlow = 0; - let summerRainDayReturnFlow = 0; - let summerRainDayEfflFlow = 0; - - let winterSumFlow = 0; - let winterChungchunSumFlow = 0; - let minWinterChungchunSumFlow = 9999999; - let maxWinterChungchunSumFlow = 0; - // 연계처리수,지하수유입량 const bscFctCd = fcltyCd; - const res2 = await fetchFlowRateModalList(bscFctCd, searchYear); // /fclt/flow/ - yearDay = res2.data?res2.data.length:1; - res2.data.forEach((item: any) => { - yearSumFlow += item.sumFlow && item.sumFlow !== 'x' ? Number(item.sumFlow.toString().trim()) : 0; - if(maxYearSumFlow < (item.sumFlow && item.sumFlow !== 'x' ? Number(item.sumFlow.toString().trim()) : 0)) { - maxYearSumFlow = item.sumFlow && item.sumFlow !== 'x' ? Number(item.sumFlow.toString().trim()) : 0; - } - if(minYearSumFlow > (item.sumFlow && item.sumFlow !== 'x' ? Number(item.sumFlow.toString().trim()) : 0)) { - minYearSumFlow = item.sumFlow && item.sumFlow !== 'x' ? Number(item.sumFlow.toString().trim()) : 0; + let res2: any = { data: [] }; + + // 이미 조회된 데이터가 있는지 확인 (연도와 시설코드가 같을 때만) + if (state.RainManage.flowRateYear === searchYear && state.RainManage.flowRateFcltyCd === fcltyCd && state.RainManage.flowRateData) { + res2 = state.RainManage.flowRateData; + } else { + res2 = await fetchFlowRateModalList(bscFctCd, searchYear); // /fclt/flow/ - 일별 유량 조회 + // 조회된 데이터 저장 + set((state: DashBoardType) => ({ + ...state, + RainManage: { + ...state.RainManage, + flowRateData: res2, + flowRateYear: searchYear, + flowRateFcltyCd: fcltyCd, + } + })); + } + + //const calData = CalcRainEffect(clone(res2.data), 'SUNNY'); + //const calRainData = CalcRainEffect(clone(res2.data), 'RAIN'); + + let cntSunnyDay = 0; // 청천일 수 + // let chungcunSumFlow = 0; // 연간 청천 순수하수 + + let rainEffectDay = 0; + yearDay = res2.data ? res2.data.length : 1; + res2.data.forEach((item: any) => { // --일별 유량 start + + const month = item.msrDt.substring(4, 6); // 월 추출 + const sumFlow = item.sumFlow && item.sumFlow !== 'x' ? Number(item.sumFlow.toString().trim()) : 0; + const inflAmnt = item.inflAmnt && item.inflAmnt !== 'x' ? Number(item.inflAmnt.toString().trim()) : 0; + const returnAmnt = item.returnAmnt && item.returnAmnt !== 'x' ? Number(item.returnAmnt.toString().trim()) : 0; + const efflAmnt = item.efflAmnt && item.efflAmnt !== 'x' ? Number(item.efflAmnt.toString().trim()) : 0; + + if (item.infoRnDay == null) item.infoRnDay = 0; + if (item.infoRnDay >= 3) { + rainEffectDay = 2; + // 강우시 + } else { + rainEffectDay--; } - // 1년 최대 총 하수유입량 - let totYearFlow = 0; - totYearFlow = (item.inflAmnt && item.inflAmnt !== 'x' ? Number(item.inflAmnt.toString().trim()) : 0) + (item.sumFlow && item.sumFlow !== 'x' ? Number(item.sumFlow.toString().trim()) : 0) + (item.returnAmnt && item.returnAmnt !== 'x' ? Number(item.returnAmnt.toString().trim()) : 0); - if(totFlowMax < totYearFlow) { - totFlowMax = totYearFlow; - } - if(totFlowMin > totYearFlow || totFlowMin === 0) { - totFlowMin = totYearFlow; + if (rainEffectDay >= 0) { + // 강우영향일 + if (month === '06' || month === '07' || month === '08') { // 하절기 강우영향일 + summerRainDay += 1; + operList.inFlow.summerRainy += inflAmnt + sumFlow + returnAmnt; + operList.inflAmt.summerRainy += inflAmnt; + operList.sumFlow.summerRainy += sumFlow; + operList.returnFlow.summerRainy += returnAmnt; + operList.efflFlow.summerRainy += efflAmnt; + } + } else if (rainEffectDay < 0) { + cntSunnyDay += 1; // 청천일수 + // 청천시 + // chungcunSumFlow += inflAmnt; // 연간 청천 순수하수 + operList.inflAmt.yearSunny += inflAmnt; // 연간 청천 순수하수 + + if (month === '06' || month === '07' || month === '08') { // 하절기 청천 + summerChungchunDay += 1; + operList.inFlow.summerSunny += inflAmnt + sumFlow + returnAmnt; + operList.inflAmt.summerSunny += inflAmnt; + operList.sumFlow.summerSunny += sumFlow; + operList.returnFlow.summerSunny += returnAmnt; + operList.efflFlow.summerSunny += efflAmnt; + } else if (month === '01' || month === '02') { // 동절기 청천 + winterChungchuDay += 1; + const inflowOne = inflAmnt + sumFlow + returnAmnt; + operList.inFlow.winterSunny += inflAmnt + sumFlow + returnAmnt; + operList.inflAmt.winterSunny += inflAmnt; + operList.sumFlow.winterSunny += sumFlow; + operList.returnFlow.winterSunny += returnAmnt; + operList.efflFlow.winterSunny += efflAmnt; + + if (operList.inFlow.winterSunnyMax < inflowOne) operList.inFlow.winterSunnyMax = inflowOne; + if (operList.inflAmt.winterSunnyMax < inflAmnt) operList.inflAmt.winterSunnyMax = inflAmnt; + if (operList.sumFlow.winterSunnyMax < sumFlow) operList.sumFlow.winterSunnyMax = sumFlow; + if (operList.returnFlow.winterSunnyMax < returnAmnt) operList.returnFlow.winterSunnyMax = returnAmnt; + if (operList.efflFlow.winterSunnyMax < efflAmnt) operList.efflFlow.winterSunnyMax = efflAmnt; + + if (operList.inFlow.winterSunnyMin > inflowOne) operList.inFlow.winterSunnyMin = inflowOne; + if (operList.inflAmt.winterSunnyMin > inflAmnt) operList.inflAmt.winterSunnyMin = inflAmnt; + if (operList.sumFlow.winterSunnyMin > sumFlow) operList.sumFlow.winterSunnyMin = sumFlow; + if (operList.returnFlow.winterSunnyMin > returnAmnt) operList.returnFlow.winterSunnyMin = returnAmnt; + if (operList.efflFlow.winterSunnyMin > efflAmnt) operList.efflFlow.winterSunnyMin = efflAmnt; + } } - if(item.msrDt.substring(4,6) === '06' || item.msrDt.substring(4,6) === '07' || item.msrDt.substring(4,6) === '08') { + const inflowOne = inflAmnt + sumFlow + returnAmnt; + operList.inFlow.yearTot += inflAmnt + sumFlow + returnAmnt; + operList.inflAmt.yearTot += inflAmnt; + operList.sumFlow.yearTot += sumFlow; + operList.returnFlow.yearTot += returnAmnt; + operList.efflFlow.yearTot += efflAmnt; + + if (operList.inFlow.yearMax < inflowOne) operList.inFlow.yearMax = inflowOne; + if (operList.inflAmt.yearMax < inflAmnt) operList.inflAmt.yearMax = inflAmnt; + if (operList.sumFlow.yearMax < sumFlow) operList.sumFlow.yearMax = sumFlow; + if (operList.returnFlow.yearMax < returnAmnt) operList.returnFlow.yearMax = returnAmnt; + if (operList.efflFlow.yearMax < efflAmnt) operList.efflFlow.yearMax = efflAmnt; + + if (operList.inFlow.yearMin > inflowOne) operList.inFlow.yearMin = inflowOne; + if (operList.inflAmt.yearMin > inflAmnt) operList.inflAmt.yearMin = inflAmnt; + if (operList.sumFlow.yearMin > sumFlow) operList.sumFlow.yearMin = sumFlow; + if (operList.returnFlow.yearMin > returnAmnt) operList.returnFlow.yearMin = returnAmnt; + if (operList.efflFlow.yearMin > efflAmnt) operList.efflFlow.yearMin = efflAmnt; + + if (month === '06' || month === '07' || month === '08') { summerDay += 1; - summerSumFlow += item.sumFlow && item.sumFlow !== 'x' ? Number(item.sumFlow.toString().trim()) : 0; - } else if(item.msrDt.substring(4,6) === '01' || item.msrDt.substring(4,6) === '02') { + operList.inFlow.summerTot += inflAmnt + sumFlow + returnAmnt; + operList.inflAmt.summerTot += inflAmnt; + operList.sumFlow.summerTot += sumFlow; + operList.returnFlow.summerTot += returnAmnt; + operList.efflFlow.summerTot += efflAmnt; + } else if (month === '01' || month === '02') { winterDay += 1; - winterSumFlow += item.sumFlow && item.sumFlow !== 'x' ? Number(item.sumFlow.toString().trim()) : 0; + operList.inFlow.winterTot += inflAmnt + sumFlow + returnAmnt; + operList.inflAmt.winterTot += inflAmnt; + operList.sumFlow.winterTot += sumFlow; + operList.returnFlow.winterTot += returnAmnt; + operList.efflFlow.winterTot += efflAmnt; } - }); + }); // --일별 유량 end - const calData = CalcRainEffect(clone(res2.data), 'SUNNY'); - const calRainData = CalcRainEffect(clone(res2.data), 'RAIN'); - - // 지하수유입량 - 청천시 기준 - let chungcunSumFlow = 0; - let sumSummerChungcunFlow = 0; - let sumWinterChungcunFlow = 0; - - // 하절기 청천시 총인설비 반류수 - let summerChungchunDayReturnAmnt = 0; - // 하절기 청천시 방류수 - let summerChungchunDayEfflAmnt = 0; + // 연간 평균 + operList.inFlow.yearAvg = (yearDay > 0) ? operList.inFlow.yearTot / yearDay : 0; + operList.inflAmt.yearAvg = (yearDay > 0) ? operList.inflAmt.yearTot / yearDay : 0; + operList.sumFlow.yearAvg = (yearDay > 0) ? operList.sumFlow.yearTot / yearDay : 0; + operList.returnFlow.yearAvg = (yearDay > 0) ? operList.returnFlow.yearTot / yearDay : 0; + operList.efflFlow.yearAvg = (yearDay > 0) ? operList.efflFlow.yearTot / yearDay : 0; + // 하절기 전기간 평균 + operList.inFlow.summerAvg = (summerDay > 0) ? operList.inFlow.summerTot / summerDay : 0; + operList.inflAmt.summerAvg = (summerDay > 0) ? operList.inflAmt.summerTot / summerDay : 0; + operList.sumFlow.summerAvg = (summerDay > 0) ? operList.sumFlow.summerTot / summerDay : 0; + operList.returnFlow.summerAvg = (summerDay > 0) ? operList.returnFlow.summerTot / summerDay : 0; + operList.efflFlow.summerAvg = (summerDay > 0) ? operList.efflFlow.summerTot / summerDay : 0; + // 하절기 강우시 평균 + operList.inFlow.summerRainy = (summerRainDay > 0) ? operList.inFlow.summerRainy / summerRainDay : 0; + operList.inflAmt.summerRainy = (summerRainDay > 0) ? operList.inflAmt.summerRainy / summerRainDay : 0; + operList.sumFlow.summerRainy = (summerRainDay > 0) ? operList.sumFlow.summerRainy / summerRainDay : 0; + operList.returnFlow.summerRainy = (summerRainDay > 0) ? operList.returnFlow.summerRainy / summerRainDay : 0; + operList.efflFlow.summerRainy = (summerRainDay > 0) ? operList.efflFlow.summerRainy / summerRainDay : 0; + // 하절기 청천시 평균 + operList.inFlow.summerSunny = (summerChungchunDay > 0) ? operList.inFlow.summerSunny / summerChungchunDay : 0; + operList.inflAmt.summerSunny = (summerChungchunDay > 0) ? operList.inflAmt.summerSunny / summerChungchunDay : 0; + operList.sumFlow.summerSunny = (summerChungchunDay > 0) ? operList.sumFlow.summerSunny / summerChungchunDay : 0; + operList.returnFlow.summerSunny = (summerChungchunDay > 0) ? operList.returnFlow.summerSunny / summerChungchunDay : 0; + operList.efflFlow.summerSunny = (summerChungchunDay > 0) ? operList.efflFlow.summerSunny / summerChungchunDay : 0; + // 동절기 전기간 평균 + operList.inFlow.winterAvg = (winterDay > 0) ? operList.inFlow.winterTot / winterDay : 0; + operList.inflAmt.winterAvg = (winterDay > 0) ? operList.inflAmt.winterTot / winterDay : 0; + operList.sumFlow.winterAvg = (winterDay > 0) ? operList.sumFlow.winterTot / winterDay : 0; + operList.returnFlow.winterAvg = (winterDay > 0) ? operList.returnFlow.winterTot / winterDay : 0; + operList.efflFlow.winterAvg = (winterDay > 0) ? operList.efflFlow.winterTot / winterDay : 0; + // 동절기 청천시 평균 + operList.inFlow.winterSunny = (winterChungchuDay > 0) ? operList.inFlow.winterSunny / winterChungchuDay : 0; + operList.inflAmt.winterSunny = (winterChungchuDay > 0) ? operList.inflAmt.winterSunny / winterChungchuDay : 0; + operList.sumFlow.winterSunny = (winterChungchuDay > 0) ? operList.sumFlow.winterSunny / winterChungchuDay : 0; + operList.returnFlow.winterSunny = (winterChungchuDay > 0) ? operList.returnFlow.winterSunny / winterChungchuDay : 0; + operList.efflFlow.winterSunny = (winterChungchuDay > 0) ? operList.efflFlow.winterSunny / winterChungchuDay : 0; - // 동절기 총 유입량 최대, 최소 - let winterChungchunTotalInflAmntMax = 0 ; - let winterChungchunTotalInflAmntMin = 9999999 ; - // 동절기 청천시 하수유입량 최대, 최소 - let winterChungchunDayFlowMax = 0 ; - let winterChungchunDayFlowMin = 9999999 ; - // 동절기 청천시 총인반류수 최대, 최소, 평균 - let winterChungchunDayReturnMax = 0 ; - let winterChungchunDayReturnMin = 9999999 ; - let winterChungchunDayReturnAvg = 0 ; - // 동절기 청천시 방류량 최대, 최소, 평균 - let winterChungchunDayEfflMax = 0 ; - let winterChungchunDayEfflMin = 9999999 ; - let winterChungchunDayEfflAvg = 0; - - // 하절기, 동절기 청천시 계산 - calData.forEach((flowItem: any) => { - chungcunSumFlow += flowItem.inflAmnt && flowItem.inflAmnt !== 'x' ? Number(flowItem.inflAmnt.toString().trim()) : 0; - - if(flowItem.msrDt.substring(4,6) ==='06' || flowItem.msrDt.substring(4,6) ==='07' || flowItem.msrDt.substring(4,6) ==='08'){ - summerChungchunDay += 1; - summerChungchunSumFlow += flowItem.sumFlow && flowItem.sumFlow !== 'x' ? Number(flowItem.sumFlow.toString().trim()) : 0; - sumSummerChungcunFlow += flowItem.inflAmnt && flowItem.inflAmnt !== 'x' ? Number(flowItem.inflAmnt.toString().trim()) : 0; - summerChungchunDayReturnAmnt += flowItem.returnAmnt && flowItem.returnAmnt !== 'x' ? Number(flowItem.returnAmnt.toString().trim()) : 0; - summerChungchunDayEfflAmnt += flowItem.efflAmnt && flowItem.efflAmnt !== 'x' ? Number(flowItem.efflAmnt.toString().trim()) : 0; - - }else if(flowItem.msrDt.substring(4,6) ==='01' || flowItem.msrDt.substring(4,6) ==='02'){ - winterChungchuDay += 1; - winterChungchunSumFlow += flowItem.sumFlow && flowItem.sumFlow !== 'x' ? Number(flowItem.sumFlow.toString().trim()) : 0; - sumWinterChungcunFlow += flowItem.inflAmnt && flowItem.inflAmnt !== 'x' ? Number(flowItem.inflAmnt.toString().trim()) : 0; - winterChungchunDayReturnAvg += flowItem.returnAmnt && flowItem.returnAmnt !== 'x' ? Number(flowItem.returnAmnt.toString().trim()) : 0; - winterChungchunDayEfflAvg += flowItem.efflAmnt && flowItem.efflAmnt !== 'x' ? Number(flowItem.efflAmnt.toString().trim()) : 0; - - let totWinterTotalInflAmnt = 0; - totWinterTotalInflAmnt = (flowItem.inflAmnt && flowItem.inflAmnt !== 'x' ? Number(flowItem.inflAmnt.toString().trim()) : 0) + (flowItem.sumFlow && flowItem.sumFlow !== 'x' ? Number(flowItem.sumFlow.toString().trim()) : 0) + (flowItem.returnAmnt && flowItem.returnAmnt !== 'x' ? Number(flowItem.returnAmnt.toString().trim()) : 0); - if(winterChungchunTotalInflAmntMax < totWinterTotalInflAmnt) { - winterChungchunTotalInflAmntMax = totWinterTotalInflAmnt; - } - if(winterChungchunTotalInflAmntMin > totWinterTotalInflAmnt || winterChungchunTotalInflAmntMin === 0) { - winterChungchunTotalInflAmntMin = totWinterTotalInflAmnt; - } - - if(winterChungchunDayFlowMin > (flowItem.inflAmnt && flowItem.inflAmnt !== 'x' ? Number(flowItem.inflAmnt.toString().trim()) : 0)) { - winterChungchunDayFlowMin = flowItem.inflAmnt && flowItem.inflAmnt !== 'x' ? Number(flowItem.inflAmnt.toString().trim()) : 0; - } - if(winterChungchunDayFlowMax < (flowItem.inflAmnt && flowItem.inflAmnt !== 'x' ? Number(flowItem.inflAmnt.toString().trim()) : 0)) { - winterChungchunDayFlowMax = flowItem.inflAmnt && flowItem.inflAmnt !== 'x' ? Number(flowItem.inflAmnt.toString().trim()) : 0; - } - - if(minWinterChungchunSumFlow > (flowItem.sumFlow && flowItem.sumFlow !== 'x' ? Number(flowItem.sumFlow.toString().trim()) : 0) ) { - minWinterChungchunSumFlow = flowItem.sumFlow && flowItem.sumFlow !== 'x' ? Number(flowItem.sumFlow.toString().trim()) : 0; - } - if(maxWinterChungchunSumFlow < (flowItem.sumFlow && flowItem.sumFlow !== 'x' ? Number(flowItem.sumFlow.toString().trim()) : 0)) { - maxWinterChungchunSumFlow = flowItem.sumFlow && flowItem.sumFlow !== 'x' ? Number(flowItem.sumFlow.toString().trim()) : 0; - } - - if(winterChungchunDayReturnMin > (flowItem.returnAmnt && flowItem.returnAmnt !== 'x' ? Number(flowItem.returnAmnt.toString().trim()) : 0)) { - winterChungchunDayReturnMin = flowItem.returnAmnt && flowItem.returnAmnt !== 'x' ? Number(flowItem.returnAmnt.toString().trim()) : 0; - } - if(winterChungchunDayReturnMax < (flowItem.returnAmnt && flowItem.returnAmnt !== 'x' ? Number(flowItem.returnAmnt.toString().trim()) : 0)) { - winterChungchunDayReturnMax = flowItem.returnAmnt && flowItem.returnAmnt !== 'x' ? Number(flowItem.returnAmnt.toString().trim()) : 0; - } - - if(winterChungchunDayEfflMin > (flowItem.efflAmnt && flowItem.efflAmnt !== 'x' ? Number(flowItem.efflAmnt.toString().trim()) : 0)) { - winterChungchunDayEfflMin = flowItem.efflAmnt && flowItem.efflAmnt !== 'x' ? Number(flowItem.efflAmnt.toString().trim()) : 0; - } - if(winterChungchunDayEfflMax < (flowItem.efflAmnt && flowItem.efflAmnt !== 'x' ? Number(flowItem.efflAmnt.toString().trim()) : 0)) { - winterChungchunDayEfflMax = flowItem.efflAmnt && flowItem.efflAmnt !== 'x' ? Number(flowItem.efflAmnt.toString().trim()) : 0; - } - } - }); - - // 하절기 강우시 계산 - calRainData.forEach((flowItem: any) => { - if(flowItem.msrDt.substring(4,6) === '06' || flowItem.msrDt.substring(4,6) === '07' || flowItem.msrDt.substring(4,6) === '08') { - summerRainDay += 1; - summerRainDaySumFlow += flowItem.sumFlow && flowItem.sumFlow !== 'x' ? Number(flowItem.sumFlow.toString().trim()) : 0; - summerRainDayFlow += flowItem.inflAmnt && flowItem.inflAmnt !== 'x' ? Number(flowItem.inflAmnt.toString().trim()) : 0; - summerRainDayReturnFlow += flowItem.returnAmnt && flowItem.returnAmnt !== 'x' ? Number(flowItem.returnAmnt.toString().trim()) : 0; - summerRainDayEfflFlow += flowItem.efflAmnt && flowItem.efflAmnt !== 'x' ? Number(flowItem.efflAmnt.toString().trim()) : 0; - } - }); - - // 연계처리수 - yearAvgSumFlow = Number((yearSumFlow&&Number(yearSumFlow)!=0)?(yearSumFlow / yearDay):''); - yearMaxSumFlow = maxYearSumFlow; - yearMinSumFlow = minYearSumFlow; - summerAll = summerSumFlow&&Number(summerSumFlow)!=0?Number(summerSumFlow / summerDay):0 - summerThree= summerRainDaySumFlow&&Number(summerRainDaySumFlow)!=0?Number(summerRainDaySumFlow / summerRainDay):0; - summerRain = summerChungchunSumFlow&&Number(summerChungchunSumFlow)!=0?Number(summerChungchunSumFlow / summerChungchunDay):0; - winterAll = winterSumFlow&&Number(winterSumFlow)!=0?Number(winterSumFlow / winterDay):0; - winterSunnyMin = minWinterChungchunSumFlow; - winterSunnyAvg = winterChungchunSumFlow&&Number(winterChungchunSumFlow)!=0?(winterChungchunSumFlow / winterChungchuDay):0; - winterSunnyMax = maxWinterChungchunSumFlow; - - // 하절기 강우시 계산 - // 하수유입량 (강우시) - summerRainInflAmnt = summerRainDayFlow&&Number(summerRainDayFlow)!=0?Number(summerRainDayFlow / summerRainDay):0; - // 총인설비방류수 (강우시) - summerRainReturnAmnt = summerRainDayReturnFlow&&Number(summerRainDayReturnFlow)!=0?Number(summerRainDayReturnFlow / summerRainDay):0; - // 방루량 (강우시) - summerRainEfflAmnt = summerRainDayEfflFlow&&summerRainDayEfflFlow!=0?summerRainDayEfflFlow / summerRainDay:0; - - // 동절기 청천시 계산 - // 하수유입량 (청천시) 평균 - winterChungchunFlowAvg = sumWinterChungcunFlow&&Number(sumWinterChungcunFlow)!=0?(sumWinterChungcunFlow / winterChungchuDay):0; - // 총인반류수 (청천시) 평균 - winterChungchunReturnAvg = winterChungchunDayReturnAvg&&Number(winterChungchunDayReturnAvg)!=0?(winterChungchunDayReturnAvg / winterChungchuDay):0; - // 방류량 (청천시) 평균 - winterChungchunEfflAvg = winterChungchunDayEfflAvg&&Number(winterChungchunDayEfflAvg)!=0?(winterChungchunDayEfflAvg / winterChungchuDay):0; - // 하수유입량 (청천시) 최대 - winterChungchunFlowMax = winterChungchunDayFlowMax; - // 총인반류수 (청천시) 최대 - winterChungchunReturnMax = winterChungchunDayReturnMax; - // 방류량 (청천시) 최대 - winterChungchunEfflMax = winterChungchunDayEfflMax; - // 하수유입량 (청천시) 최소 - winterChungchunFlowMin = winterChungchunDayFlowMin; - // 총인반류수 (청천시) 최소 - winterChungchunReturnMin = winterChungchunDayReturnMin; - // 방류량 (청천시) 최소 - winterChungchunEfflMin = winterChungchunDayEfflMin - - operationalStatusList[0].yearAvg = roundUp(Number(operationalStatusList[0].yearAvg), 0) + roundUp(yearAvgSumFlow, 0); - operationalStatusList[0].yearMax = roundUp(totFlowMax, 0); // + roundUp(yearMaxSumFlow, 0); - operationalStatusList[0].yearMin = roundUp(totFlowMin, 0); - operationalStatusList[0].summerAll = roundUp(Number(operationalStatusList[0].summerAll), 0) + roundUp(summerAll, 0); - operationalStatusList[0].summerThree = roundUp(Number(operationalStatusList[0].summerThree), 0) + roundUp(summerThree, 0); - operationalStatusList[0].summerRain = roundUp(Number(operationalStatusList[0].summerRain), 0) + roundUp(summerRain, 0); - operationalStatusList[0].winterAll = roundUp(Number(operationalStatusList[0].winterAll), 0) + roundUp(winterAll, 0); - operationalStatusList[0].winterSunnyMin = roundUp(winterChungchunTotalInflAmntMin, 0); - operationalStatusList[0].winterSunnyMax = roundUp(winterChungchunTotalInflAmntMax, 0); - operationalStatusList[0].winterSunnyAvg = roundUp(Number(operationalStatusList[0].winterSunnyAvg), 0) + roundUp(winterSunnyAvg, 0); + // 연간 유입 평균 = 하수유입량(순수하수유입량) + 연계처리량 + 반류수유량(m³/일) + operationalStatusList[0].yearAvg = roundUp(operList.inFlow.yearAvg, 0); // 연간 유입 평균 = 하수유입량(순수하수유입량) + 연계처리량 + 반류수유량(m³/일) + operationalStatusList[0].yearMax = roundUp(operList.inFlow.yearMax, 0); // 연간 유입 최대 + operationalStatusList[0].yearMin = roundUp(operList.inFlow.yearMin, 0); // 연간 유입 최소 + operationalStatusList[0].summerAll = roundUp(operList.inFlow.summerAvg, 0); // 하절기 유입 전기간 + operationalStatusList[0].summerThree = roundUp(operList.inFlow.summerRainy, 0); // 하절기 유입 강우시(강우영향일) + operationalStatusList[0].summerRain = roundUp(operList.inFlow.summerSunny, 0); // 하절기 유입 청천시 + operationalStatusList[0].winterAll = roundUp(operList.inFlow.winterAvg, 0); // 동절기 유입 전기간 + operationalStatusList[0].winterSunnyMin = roundUp(operList.inFlow.winterSunnyMin, 0); // 동절기 유입 최소 + operationalStatusList[0].winterSunnyMax = roundUp(operList.inFlow.winterSunnyMax, 0); // 동절기 유입 최대 + operationalStatusList[0].winterSunnyAvg = roundUp(operList.inFlow.winterAvg, 0); // 동절기 유입 평균 + // 청천시 유입(순수하수) 평균 + const usage = operList.inflAmt.yearSunny / cntSunnyDay; // 연간 청천 순수하수 평균 + const summerUsage = operList.inflAmt.summerSunny; // 하절기 청천 순수하수 평균 + const winterUsage = operList.inflAmt.winterSunny; // 동절기 청천 순수하수 평균 + // 물사용량 = 유입평균 * 0.65 + const wtrUsg = operList.inflAmt.yearAvg * 0.65; // 연간 물사용량 평균 + const wtrUsgSummer = operList.inflAmt.summerAvg * 0.65; // 하절기 물사용량 평균 + const wtrUsgWinter = operList.inflAmt.winterAvg * 0.65; // 동절기 물사용량 평균 // 지하수유입량 - const usage = chungcunSumFlow / calData.length; - const summerUsage = sumSummerChungcunFlow / summerChungchunDay; - const winterUsage = sumWinterChungcunFlow / winterChungchuDay; - - // 임시 계산변수값들 메인에 대입 - summerChungchunFlow = summerUsage; - summerChungchunReturnAmnt = summerChungchunDayReturnAmnt / summerChungchunDay; - summerChungchunEfflAmnt = summerChungchunDayEfflAmnt / summerChungchunDay; - - operationalStatusList[0].groundWaterAvg = Math.round((usage-(usage*0.65*OSU_PERCENT)) * 100) / 100; - operationalStatusList[0].groundWaterSummer = Math.round((summerUsage-(summerUsage*0.65*OSU_PERCENT)) * 100) / 100; - operationalStatusList[0].groundWaterWinter = Math.round((winterUsage-(winterUsage*0.65*OSU_PERCENT)) * 100) / 100; - - //operationalStatusList[0].groundWaterAvg = calData.length === 0 ? '' : Number(calData[0].inflAmnt) - (Number(calData[0].inflAmnt)*0.65*0.8); - //operationalStatusList[0].groundWaterSummer = calData.length === 0 ? '' : Number(calData[0].inflAmnt) - (Number(calData[0].inflAmnt)*0.65*0.8); - //operationalStatusList[0].groundWaterWinter = calData.length === 0 ? '' : Number(calData[0].inflAmnt) - (Number(calData[0].inflAmnt)*0.65*0.8); + operationalStatusList[0].groundWaterAvg = Math.round((usage - (wtrUsg * OSU_PERCENT)) * 100) / 100; + operationalStatusList[0].groundWaterSummer = Math.round((summerUsage - (wtrUsgSummer * OSU_PERCENT)) * 100) / 100; + operationalStatusList[0].groundWaterWinter = Math.round((winterUsage - (wtrUsgWinter * OSU_PERCENT)) * 100) / 100; // 오염물질 // RDII 추정 = 하절기(6~8월) 중 최저 유입 BOD일때 유입유량 x ((동절기 청천시 유입BOD - 하절기(6~8월) 중 최저 유입BOD) / (동절기 청천시 유입BOD)) - const resp = await fetchMainGraph(searchYear, fcltyCd); - const wqltData = resp.data.wqltObjs; - const wqltDataTot = CalcRainEffect(clone(wqltData)); - const wqltDataSunny = CalcRainEffect(clone(wqltData), 'SUNNY'); + // const resp = await fetchMainGraph(searchYear, fcltyCd); + const resp = mainGraphData; + if (resp && resp.data && resp.data.wqltObjs && resp.data.wqltObjs.length > 0) { + const wqltData = resp.data.wqltObjs; + const wqltDataTot = CalcRainEffect(clone(wqltData)); + const wqltDataSunny = CalcRainEffect(clone(wqltData), 'SUNNY'); - let minSummerBod = 99999999; - let minSummerBodInfl = 99999999; - let winterCnt = 0; - let winterBodSunny = 0; - let rdiRain = 0; + let minSummerBod = 99999999; + let minSummerBodInfl = 99999999; + let winterCnt = 0; + let winterBodSunny = 0; + let rdiRain = 0; - const summerMonth = ['06', '07', '08']; - const winterMonth = ['01', '02']; + const summerMonth = ['06', '07', '08']; + const winterMonth = ['01', '02']; - wqltDataTot.filter(item=>summerMonth.includes(item.msrDt.toString().replaceAll('-','').substring(4,6))).forEach(item2 => { - if(Number(item2.infoRnDay)>=3){ - const inflBod = Number(item2.inflAssctBfrWqltBod); - const infl = Number(item2.inflAmnt); - const rain = Number(item2.infoRnDay); - if(inflBod < minSummerBod){ - minSummerBodInfl = infl; - minSummerBod = inflBod; - rdiRain = rain; + wqltDataTot.filter(item => summerMonth.includes(item.msrDt.toString().replaceAll('-', '').substring(4, 6))).forEach(item2 => { + if (Number(item2.infoRnDay) >= 3) { // 3mm 이상 강우일 + const inflBod = Number(item2.inflAssctBfrWqltBod); //유입연계전수질_BOD(mg/L) + const infl = Number(item2.inflAmnt); //실제유입량(반류수제거)(m³/일) + const rain = Number(item2.infoRnDay); //강우량 + if (inflBod < minSummerBod) { + minSummerBodInfl = infl; + minSummerBod = inflBod; + rdiRain = rain; // 하절기 최소 bod일때의 일강수량 + } } - } - }); + }); - wqltDataSunny.filter(item=>winterMonth.includes(item.msrDt.toString().replaceAll('-','').substring(4,6))).forEach(item2 => { - winterBodSunny += Number(item2.inflAssctBfrWqltBod); - winterCnt += 1; - }); + wqltDataSunny.filter(item => winterMonth.includes(item.msrDt.toString().replaceAll('-', '').substring(4, 6))).forEach(item2 => { + winterBodSunny += Number(item2.inflAssctBfrWqltBod); // 동절기 청천시 유입BOD(mg/L) + winterCnt += 1; + }); - const roundMinSummerBodInfl = Number(roundNumber(minSummerBodInfl, 0).toString().replaceAll(',','')); - const roundMinSummerBod = Number(roundNumber(minSummerBod, 1).toString().replaceAll(',','')); - const roundWinterBodSunny = Number(roundNumber(winterBodSunny, 1).toString().replaceAll(',','')); + const roundMinSummerBodInfl = Number(roundNumber(minSummerBodInfl, 0).toString().replaceAll(',', '')); + const roundMinSummerBod = Number(roundNumber(minSummerBod, 1).toString().replaceAll(',', '')); + const roundWinterBodSunny = Number(roundNumber(winterBodSunny, 1).toString().replaceAll(',', '')); - const winterBodAvg = Number(roundNumber((roundWinterBodSunny / winterCnt), 1).toString().replaceAll(',','')); - // 하절기(6~8월) 중 최저 유입 BOD일때 유입유량 x ((동절기 청천시 유입BOD - 하절기(6~8월) 중 최저 유입BOD) / (동절기 청천시 유입BOD)) - const winMinusSum = winterBodAvg - roundMinSummerBod; + const winterBodAvg = Number(roundNumber((roundWinterBodSunny / winterCnt), 1).toString().replaceAll(',', '')); + // 하절기(6~8월) 중 최저 유입 BOD일때 유입유량 x ((동절기 청천시 유입BOD - 하절기(6~8월) 중 최저 유입BOD) / (동절기 청천시 유입BOD)) + const winMinusSum = winterBodAvg - roundMinSummerBod; - const rdiValue = winMinusSum===0 ? 0 : roundMinSummerBodInfl * (winMinusSum / winterBodAvg ); - - rdiPercent = (rdiValue / minSummerBodInfl) * 100; + const rdiValue = winMinusSum === 0 ? 0 : roundMinSummerBodInfl * (winMinusSum / winterBodAvg); - operationalStatusList[0].rdi = rdiValue; - operationalStatusList[0].rdiRain = rdiRain; - /* const summerLowFlow = data.summerBod&&data.summerBod.INFL_AMNT&&data.summerBod.INFL_AMNT!='null'?Number(data.summerBod.INFL_AMNT):0; - const winterAvgBod = data.winterSunny&&data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD&&data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD!='null'?Number(data.winterSunny.AVG_INFL_ASSCT_BFR_WQLT_BOD):0; - const summerLowBod = data.summerBod&&data.summerBod.MIN_INFL_ASSCT_BFR_WQLT_BOD&&data.summerBod.MIN_INFL_ASSCT_BFR_WQLT_BOD!='null'?Number(data.summerBod.MIN_INFL_ASSCT_BFR_WQLT_BOD):0; - const summerLowRain = data.summerBod&&data.summerBod.INFO_RN_DAY&&data.summerBod.INFO_RN_DAY!='null'?Number(data.summerBod.INFO_RN_DAY):0; - operationalStatusList[0].rdi = winterAvgBod===0?0:roundUp(summerLowFlow * ((winterAvgBod - summerLowBod) / winterAvgBod), 2); - operationalStatusList[0].rdiRain = summerLowRain; */ - } + rdiPercent = (rdiValue / minSummerBodInfl) * 100; - if(operationalStatusList[0] && operationalStatusList[7]){ - operationalStatusList[7].groundWaterAvg = operationalStatusList[0].groundWaterAvg === null ? '' : data.yearData && data.yearData.AVG_INFL_AMNT === 0 ? '' : Math.round((Number(operationalStatusList[0].groundWaterAvg.toString().replaceAll(',', '')) / (data.yearData && data.yearData.AVG_INFL_AMNT ? Number(data.yearData.AVG_INFL_AMNT.toString().replaceAll(',','')) : 0) * 100) * 10) / 10+'%'; - operationalStatusList[7].groundWaterSummer = operationalStatusList[0].groundWaterSummer === null ? '' : data.summer && data.summer.AVG_INFL_AMNT === 0 ? '' : Math.round((Number(operationalStatusList[0].groundWaterSummer.toString().replaceAll(',','')) / (data.summer && data.summer.AVG_INFL_AMNT ? Number(data.summer.AVG_INFL_AMNT.toString().replaceAll(',','')) : 0) * 100) * 10) / 10+'%'; - operationalStatusList[7].groundWaterWinter = operationalStatusList[0].groundWaterWinter === null ? '' : data.winter && data.winter.AVG_INFL_AMNT === 0 ? '' : Math.round((Number(operationalStatusList[0].groundWaterWinter.toString().replaceAll(',','')) / (data.winter && data.winter.AVG_INFL_AMNT ? Number(data.winter.AVG_INFL_AMNT.toString().replaceAll(',','')) : 0) * 100) * 10) / 10+'%'; - operationalStatusList[7].rdi = roundNumber(rdiPercent, 1)+'%'; - /* operationalStatusList[7].groundWaterAvg = operationalStatusList[0].groundWaterAvg === null ? '' : Number(Number(operationalStatusList[0].groundWaterAvg) / Number(data.yearData.AVG_INFL_AMNT)) * 100; - operationalStatusList[7].groundWaterSummer = operationalStatusList[0].groundWaterSummer === null ? '' : Number(Number(operationalStatusList[0].groundWaterSummer) / Number(data.summer.AVG_INFL_AMNT)) * 100; - operationalStatusList[7].groundWaterWinter = operationalStatusList[0].groundWaterWinter === null ? '' : Number(Number(operationalStatusList[0].groundWaterWinter) / Number(data.winter.AVG_INFL_AMNT)) * 100;*/ - } - - for(let i=1; i<operationalStatusList.length; i++){ - const txtArray = ['1', 'INFL_AMNT', 'SUM_FLOW', 'RETURN_AMNT', 'EFFL_AMNT', 'INFL_ASSCT_BFR_WQLT_BOD', 'EFFL_WQLT_BOD', 'INFL_ASSCT_BFR_WQLT_TN', 'EFFL_WQLT_TN', 'INFL_ASSCT_BFR_WQLT_TP', 'EFFL_WQLT_TP', 'TP_INFL_WQLT_TP', 'EFFL_WQLT_TP']; - if(operationalStatusList[i]){ - if(i < 7){ - changeValue(operationalStatusList[i], data, txtArray[i]); - if(i < 5){ - dataFormSetting(operationalStatusList[i]!); - }else{ - dataFormSettingWqlty(operationalStatusList[i]!); - } - - }else{ - calcValue(operationalStatusList[i], data, txtArray[i]); - } + operationalStatusList[0].rdi = rdiValue; // RDII 추정 + operationalStatusList[0].rdiRain = rdiRain; // RDII 추정시 일강수량(mm) } } - // 순수하수량 - if(operationalStatusList[1]){ - operationalStatusList[1].summerThree = summerRainInflAmnt; - operationalStatusList[1].summerRain = summerChungchunFlow; - operationalStatusList[1].winterSunnyAvg = winterChungchunFlowAvg; - operationalStatusList[1].winterSunnyMax = winterChungchunFlowMax; - operationalStatusList[1].winterSunnyMin = winterChungchunFlowMin; - dataFormSetting(operationalStatusList[1]); + if (operationalStatusList[0] && operationalStatusList[7]) { + // operationalStatusList[7].groundWaterAvg = operationalStatusList[0].groundWaterAvg === null ? '' : operList.inFlow.yearTot && data.yearData.AVG_INFL_AMNT === 0 ? '' : Math.round((Number(operationalStatusList[0].groundWaterAvg.toString().replaceAll(',', '')) / (data.yearData && data.yearData.AVG_INFL_AMNT ? Number(data.yearData.AVG_INFL_AMNT.toString().replaceAll(',', '')) : 0) * 100) * 10) / 10 + '%'; + // operationalStatusList[7].groundWaterSummer = operationalStatusList[0].groundWaterSummer === null ? '' : data.summer && data.summer.AVG_INFL_AMNT === 0 ? '' : Math.round((Number(operationalStatusList[0].groundWaterSummer.toString().replaceAll(',', '')) / (data.summer && data.summer.AVG_INFL_AMNT ? Number(data.summer.AVG_INFL_AMNT.toString().replaceAll(',', '')) : 0) * 100) * 10) / 10 + '%'; + // operationalStatusList[7].groundWaterWinter = operationalStatusList[0].groundWaterWinter === null ? '' : data.winter && data.winter.AVG_INFL_AMNT === 0 ? '' : Math.round((Number(operationalStatusList[0].groundWaterWinter.toString().replaceAll(',', '')) / (data.winter && data.winter.AVG_INFL_AMNT ? Number(data.winter.AVG_INFL_AMNT.toString().replaceAll(',', '')) : 0) * 100) * 10) / 10 + '%'; + operationalStatusList[7].groundWaterAvg = operList.inflAmt.yearAvg === 0 ? '' : Math.round((Number(operationalStatusList[0].groundWaterAvg.toString().replaceAll(',', '')) / operList.inflAmt.yearAvg * 100) * 10) / 10 + '%'; + operationalStatusList[7].groundWaterSummer = operList.inflAmt.summerAvg === 0 ? '' : Math.round((Number(operationalStatusList[0].groundWaterSummer.toString().replaceAll(',', '')) / operList.inflAmt.summerAvg * 100) * 10) / 10 + '%'; + operationalStatusList[7].groundWaterWinter = operList.inflAmt.winterAvg === 0 ? '' : Math.round((Number(operationalStatusList[0].groundWaterWinter.toString().replaceAll(',', '')) / operList.inflAmt.winterAvg * 100) * 10) / 10 + '%'; + operationalStatusList[7].rdi = roundNumber(rdiPercent, 1) + '%'; } - // 연계처리수 - if(operationalStatusList[2]){ - operationalStatusList[2].yearAvg = yearAvgSumFlow; - operationalStatusList[2].yearMax = yearMaxSumFlow; - operationalStatusList[2].yearMin = yearMinSumFlow; - operationalStatusList[2].summerAll = summerAll; - operationalStatusList[2].summerThree = summerThree; - operationalStatusList[2].summerRain = summerRain; - operationalStatusList[2].winterAll = winterAll; - operationalStatusList[2].winterSunnyMin = winterSunnyMin; - operationalStatusList[2].winterSunnyAvg = winterSunnyAvg; - operationalStatusList[2].winterSunnyMax = winterSunnyMax; - dataFormSetting(operationalStatusList[2]); - } + // 순수하수량(1), 연계처리수(2), 총인설비방류수(3), 방류수(4) 데이터 매핑 + const flowMappings = [ + { index: 0, source: operList.inFlow }, + { index: 1, source: operList.inflAmt }, + { index: 2, source: operList.sumFlow }, + { index: 3, source: operList.returnFlow }, + { index: 4, source: operList.efflFlow }, + ]; + flowMappings.forEach(({ index, source }) => { + const item = operationalStatusList[index]; + if (item) { + item.yearAvg = source.yearAvg; + item.yearMax = source.yearMax; + item.yearMin = source.yearMin; + item.summerAll = source.summerAvg; + item.summerThree = source.summerRainy; + item.summerRain = source.summerSunny; + item.winterSunnyAvg = source.winterSunny; + item.winterSunnyMax = source.winterSunnyMax; + item.winterSunnyMin = source.winterSunnyMin; + item.winterAll = source.winterAvg; + dataFormSetting(item); + } + }); - // 총인설비방류수 - if(operationalStatusList[3]){ - operationalStatusList[3].summerThree = summerRainReturnAmnt; - operationalStatusList[3].summerRain = summerChungchunReturnAmnt; - operationalStatusList[3].winterSunnyAvg = winterChungchunReturnAvg; - operationalStatusList[3].winterSunnyMax = winterChungchunReturnMax; - operationalStatusList[3].winterSunnyMin = winterChungchunReturnMin; - dataFormSetting(operationalStatusList[3]); - } - - // 방류수 - if(operationalStatusList[4]){ - operationalStatusList[4].summerThree = summerRainEfflAmnt; - operationalStatusList[4].summerRain = summerChungchunEfflAmnt; - operationalStatusList[4].winterSunnyAvg = winterChungchunEfflAvg; - operationalStatusList[4].winterSunnyMax = winterChungchunEfflMax; - operationalStatusList[4].winterSunnyMin = winterChungchunEfflMin; - dataFormSetting(operationalStatusList[4]); - } - - if (operationalStatusList[0]) { - // 안전하게 숫자 변환하는 함수 - const toNum = (val) => { - if (val === null || val === undefined || val === '' || val === '0') return 0; - return Number(val.toString().replaceAll(',', '')) || 0; - }; - - operationalStatusList[0].summerAll = - toNum(operationalStatusList[1]?.summerAll) + - toNum(operationalStatusList[2]?.summerAll) + - toNum(operationalStatusList[3]?.summerAll); - - operationalStatusList[0].summerThree = - toNum(operationalStatusList[1]?.summerThree) + - toNum(operationalStatusList[2]?.summerThree) + - toNum(operationalStatusList[3]?.summerThree); - - operationalStatusList[0].summerRain = - toNum(operationalStatusList[1]?.summerRain) + - toNum(operationalStatusList[2]?.summerRain) + - toNum(operationalStatusList[3]?.summerRain); - - operationalStatusList[0].winterAll = - toNum(operationalStatusList[1]?.winterAll) + - toNum(operationalStatusList[2]?.winterAll) + - toNum(operationalStatusList[3]?.winterAll); - - operationalStatusList[0].winterSunnyAvg = - toNum(operationalStatusList[1]?.winterSunnyAvg) + - toNum(operationalStatusList[2]?.winterSunnyAvg) + - toNum(operationalStatusList[3]?.winterSunnyAvg); - - dataFormSetting(operationalStatusList[0]); - } } - const rtnData = await calcWaterQltyData(operationalStatusList, searchYear, fcltyCd); + const rtnData = await calcWaterQltyData(operationalStatusList, searchYear, fcltyCd, mainGraphData); // 수질 데이터 청천시, 강우시 재계산 + let tpFctYn = 'N'; + if (mainGraphData.data.wqltObjs && mainGraphData.data.wqltObjs.length > 0) + tpFctYn = mainGraphData.data.wqltObjs?.[0].tpFctYn; // 총인설비가 없을 경우 제외 - console.log('총인시설 여부 : ', data.yearData?.TP_FCT_YN); + console.log('총인시설 여부 : ', tpFctYn); - console.log('rtnData : ', rtnData); - if(data.yearData?.TP_FCT_YN !== 'Y') { + // console.log('rtnData : ', rtnData); + if (tpFctYn !== 'Y') { operationalStatusList = rtnData.slice(0, 11); - }else{ + } else { operationalStatusList = rtnData; } set((state: DashBoardType) => ({ @@ -1898,13 +1817,33 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) OperationStatus: { ...state.OperationStatus, operationalStatusList: operationalStatusList, - operationalStatusCount: res.count, - isLoadingOperationStatus : false, + operationalStatusCount: 0, + isLoadingOperationStatus: false, }, })) }, - + searchMainGraph: async (searchYear: string, fcltyCd: string) => { + const state = get(); + let res: any = null; + + // 이미 조회된 데이터가 있는지 확인 + if (state.RainManage.mainGraphYear === searchYear && state.RainManage.mainGraphFcltyCd === fcltyCd && state.RainManage.mainGraphData) { + res = state.RainManage.mainGraphData; + } else { + res = await fetchMainGraph(searchYear, fcltyCd); + // 데이터 저장 + set((state: DashBoardType) => ({ + ...state, + RainManage: { + ...state.RainManage, + mainGraphData: res, + mainGraphYear: searchYear, + mainGraphFcltyCd: fcltyCd, + } + })); + } + set((state: DashBoardType) => ({ ...state, MainGraph: { @@ -1912,7 +1851,7 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) isLoadingMainGraph: true, }, })); - const res = await fetchMainGraph(searchYear, fcltyCd); + // const res = await fetchMainGraph(searchYear, fcltyCd); const res2 = await fetchFacilityWaterQltyInfo(fcltyCd); let chungcunSumFlow = 0; @@ -1920,14 +1859,14 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) calData.forEach((flowItem: any) => { chungcunSumFlow += flowItem.inflAmnt && flowItem.inflAmnt !== 'x' ? Number(flowItem.inflAmnt.toString().trim()) : 0; }); - const usage = chungcunSumFlow&&chungcunSumFlow>0?chungcunSumFlow / calData.length:0; - + const usage = chungcunSumFlow && chungcunSumFlow > 0 ? chungcunSumFlow / calData.length : 0; + //bscFctCd === '11200PB001R') 중랑 - 테스트할 때 코드넣고 해야 보기편해요~ /* if(bscFctCd === '11200PB001R') { console.log('calData.length : ', calData.length); console.log('usage : ', usage, 'summerUsage : ', summerUsage, 'winterUsage : ', winterUsage); } */ - const underWaterInputYear = Math.round((usage-(usage*0.65*OSU_PERCENT)) * 1) / 1; + const underWaterInputYear = Math.round((usage - (usage * 0.65 * OSU_PERCENT)) * 1) / 1; // 법적방류수질 데이터 const sortedWaterQltyInfo = [...res2].sort((a, b) => { @@ -1935,17 +1874,17 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) const numA = Number(a.extnsSeqno); const numB = Number(b.extnsSeqno); if (!isNaN(numA) && !isNaN(numB)) { - return numA - numB; + return numA - numB; } // 숫자 변환이 실패하면 문자열로 비교 return String(a.extnsSeqno).localeCompare(String(b.extnsSeqno)); }); - const waterQltyStandartd : any = sortedWaterQltyInfo && sortedWaterQltyInfo.length > 0 + const waterQltyStandartd: any = sortedWaterQltyInfo && sortedWaterQltyInfo.length > 0 ? sortedWaterQltyInfo[sortedWaterQltyInfo.length - 1] : []; - if(res == null || res.data == null || res.data.length === 0) { + if (res == null || res.data == null || res.data.length === 0) { set((state: DashBoardType) => ({ ...state, MainGraph: { @@ -1959,61 +1898,61 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) chart_efflTn: [], chart_inflBod: [], chart_efflBod: [], - waterQltyStandartd : [], - isLoadingMainGraph : false, + waterQltyStandartd: [], + isLoadingMainGraph: false, } })); return; } - + const chart_inflowRain = res.data.wqltObjs.map((item: FacilityWaterQltyProps) => ({ - label: item.msrDt.substring(0, 4)+"-"+item.msrDt.substring(4, 6)+"-"+item.msrDt.substring(6, 8), + label: item.msrDt.substring(0, 4) + "-" + item.msrDt.substring(4, 6) + "-" + item.msrDt.substring(6, 8), barVal0: item.infoRnDay ?? 0, lineVal0: underWaterInputYear, lineVal1: item.inflAmnt ?? 0, })); - + const chart_bodRain = res.data.wqltObjs.map((item: FacilityWaterQltyProps) => ({ - label: item.msrDt.substring(0, 4)+"-"+item.msrDt.substring(4, 6)+"-"+item.msrDt.substring(6, 8), + label: item.msrDt.substring(0, 4) + "-" + item.msrDt.substring(4, 6) + "-" + item.msrDt.substring(6, 8), barVal0: item.infoRnDay ?? 0, lineVal0: item.inflAssctBfrWqltBod ?? 0, })); const chart_inflTp = res.data.wqltObjs.map((item: FacilityWaterQltyProps) => ({ - label: item.msrDt.substring(0, 4)+"-"+item.msrDt.substring(4, 6)+"-"+item.msrDt.substring(6, 8), - val : item.inflAssctBfrWqltTp ?? 0 + label: item.msrDt.substring(0, 4) + "-" + item.msrDt.substring(4, 6) + "-" + item.msrDt.substring(6, 8), + val: item.inflAssctBfrWqltTp ?? 0 })); - + const chart_chongInTp = res.data.wqltObjs.map((item: FacilityWaterQltyProps) => ({ - label: item.msrDt.substring(0, 4)+"-"+item.msrDt.substring(4, 6)+"-"+item.msrDt.substring(6, 8), - val : Number(item.tpInflWqltTp) == 0 ? null : item.tpInflWqltTp + label: item.msrDt.substring(0, 4) + "-" + item.msrDt.substring(4, 6) + "-" + item.msrDt.substring(6, 8), + val: Number(item.tpInflWqltTp) == 0 ? null : item.tpInflWqltTp })); - + const chart_efflTp = res.data.wqltObjs.map((item: FacilityWaterQltyProps) => ({ - label: item.msrDt.substring(0, 4)+"-"+item.msrDt.substring(4, 6)+"-"+item.msrDt.substring(6, 8), - val : item.efflWqltTp ?? 0 + label: item.msrDt.substring(0, 4) + "-" + item.msrDt.substring(4, 6) + "-" + item.msrDt.substring(6, 8), + val: item.efflWqltTp ?? 0 })); const chart_inflTn = res.data.wqltObjs.map((item: FacilityWaterQltyProps) => ({ - label: item.msrDt.substring(0, 4)+"-"+item.msrDt.substring(4, 6)+"-"+item.msrDt.substring(6, 8), - val : item.inflAssctBfrWqltTn ?? 0 + label: item.msrDt.substring(0, 4) + "-" + item.msrDt.substring(4, 6) + "-" + item.msrDt.substring(6, 8), + val: item.inflAssctBfrWqltTn ?? 0 })); const chart_efflTn = res.data.wqltObjs.map((item: FacilityWaterQltyProps) => ({ - label: item.msrDt.substring(0, 4)+"-"+item.msrDt.substring(4, 6)+"-"+item.msrDt.substring(6, 8), - val : item.efflWqltTn ?? 0 + label: item.msrDt.substring(0, 4) + "-" + item.msrDt.substring(4, 6) + "-" + item.msrDt.substring(6, 8), + val: item.efflWqltTn ?? 0 })); const chart_inflBod = res.data.wqltObjs.map((item: FacilityWaterQltyProps) => ({ - label: item.msrDt.substring(0, 4)+"-"+item.msrDt.substring(4, 6)+"-"+item.msrDt.substring(6, 8), - val : item.inflAssctBfrWqltBod ?? 0 + label: item.msrDt.substring(0, 4) + "-" + item.msrDt.substring(4, 6) + "-" + item.msrDt.substring(6, 8), + val: item.inflAssctBfrWqltBod ?? 0 })); - + const chart_efflBod = res.data.wqltObjs.map((item: FacilityWaterQltyProps) => ({ - label: item.msrDt.substring(0, 4)+"-"+item.msrDt.substring(4, 6)+"-"+item.msrDt.substring(6, 8), - val : item.efflWqltBod ?? 0 + label: item.msrDt.substring(0, 4) + "-" + item.msrDt.substring(4, 6) + "-" + item.msrDt.substring(6, 8), + val: item.efflWqltBod ?? 0 })); - + set((state: DashBoardType) => ({ ...state, MainGraph: { @@ -2021,19 +1960,55 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) chart_inflowRain: chart_inflowRain, chart_bodRain: chart_bodRain, chart_inflTp: chart_inflTp, - chart_chongInTp : chart_chongInTp, + chart_chongInTp: chart_chongInTp, chart_efflTp: chart_efflTp, chart_inflTn: chart_inflTn, chart_efflTn: chart_efflTn, chart_inflBod: chart_inflBod, chart_efflBod: chart_efflBod, - waterQltyStandartd : waterQltyStandartd, - isLoadingMainGraph : false, + waterQltyStandartd: waterQltyStandartd, + isLoadingMainGraph: false, }, })) }, - searchRainManage: async (searchYear: string, fcltyCd: string, isExcel : boolean) => { + searchRainManage: async (searchYear: string, fcltyCd: string, isExcel: boolean) => { + const state = get(); + let res: any = null; + + if (state.RainManage.mainGraphYear === searchYear && state.RainManage.mainGraphFcltyCd === fcltyCd && state.RainManage.mainGraphData) { + res = state.RainManage.mainGraphData; + } else { + res = await fetchMainGraph(searchYear, fcltyCd); + set((state: DashBoardType) => ({ + ...state, + RainManage: { + ...state.RainManage, + mainGraphData: res, + mainGraphYear: searchYear, + mainGraphFcltyCd: fcltyCd, + } + })); + } + + let res2: any = { data: [] }; + // 이미 조회된 데이터가 있는지 확인 (연도와 시설코드가 같을 때만) + if (state.RainManage.flowRateYear === searchYear && state.RainManage.flowRateFcltyCd === fcltyCd && state.RainManage.flowRateData) { + res2 = state.RainManage.flowRateData; + } else { + res2 = await fetchFlowRateModalList(fcltyCd, searchYear); // /fclt/flow/ - 일별 유량 조회 + // 조회된 데이터 저장 + set((state: DashBoardType) => ({ + ...state, + RainManage: { + ...state.RainManage, + flowRateData: res2, + flowRateYear: searchYear, + flowRateFcltyCd: fcltyCd, + } + })); + } + set((state: DashBoardType) => ({ ...state, RainManage: { @@ -2041,14 +2016,14 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) isLoadingRainManage: true, }, })); - const res = await fetchMainGraph(searchYear, fcltyCd); - if(res == null || res.data.wqltObjs == null || res.data.wqltObjs.length === 0) { + // const res = await fetchMainGraph(searchYear, fcltyCd); + if (res == null || res.data.wqltObjs == null || res.data.wqltObjs.length === 0) { set((state: DashBoardType) => ({ ...state, RainManage: { ...state.RainManage, rainManageList: [], - isLoadingRainManage : false, + isLoadingRainManage: false, } })); return; @@ -2057,10 +2032,9 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) const itemList = ['계', '3~10mm 미만', '10~20mm 미만', '20~30mm 미만', '30~40mm 미만', '40~50mm 미만', '50~60mm 미만', '60mm 이상']; const rainRate = ['0', '0', '0', '0', '0', '0', '0', '0']; - const rainDate = [0, 0, 0, 0, 0, 0, 0, 0]; - const inflAmtCnt = [0, 0, 0, 0, 0, 0, 0, 0]; - const inflAmnt = [0, 0, 0, 0, 0, 0, 0, 0]; - const fcltyCpct = [0, 0, 0, 0, 0, 0, 0, 0]; + const rainDate: number[] = [0, 0, 0, 0, 0, 0, 0, 0]; + const inflAmnt: number[] = [0, 0, 0, 0, 0, 0, 0, 0]; + const fcltyCpct: number[] = [0, 0, 0, 0, 0, 0, 0, 0]; const dsInflWqltBod: number[] = [0, 0, 0, 0, 0, 0, 0, 0]; const dsInflWqltCod: number[] = [0, 0, 0, 0, 0, 0, 0, 0]; const dsInflWqltSs: number[] = [0, 0, 0, 0, 0, 0, 0, 0]; @@ -2092,7 +2066,7 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) const rainEffectList = CalcRainEffect(res.data.wqltObjs, 'RAIN_DAY'); // Process the data and create rain management list - const totItemCnt = rainEffectList?rainEffectList.length : 1; + const totItemCnt = rainEffectList ? rainEffectList.length : 1; let totInflAssctBfrWqltBod = 0; let totInflAssctBfrWqltToc = 0; let totInflAssctBfrWqltSs = 0; @@ -2103,6 +2077,7 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) let totEfflWqltSs = 0; let totEfflWqltTn = 0; let totEfflWqltTp = 0; + let totInflAmnt = 0; rainEffectList.forEach((item: RainManageListProps) => { totInflAssctBfrWqltBod += item.inflAssctBfrWqltBod ? Number(item.inflAssctBfrWqltBod.toString().trim()) : 0; @@ -2115,13 +2090,13 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) totEfflWqltSs += item.efflWqltSs ? Number(item.efflWqltSs.toString().trim()) : 0; totEfflWqltTn += item.efflWqltTn ? Number(item.efflWqltTn.toString().trim()) : 0; totEfflWqltTp += item.efflWqltTp ? Number(item.efflWqltTp.toString().trim()) : 0; + totInflAmnt += item.inflAmnt ? Number(item.inflAmnt.toString().trim()) : 0; - const toBeRain = item.toBeInfoRnDay ? Number(item.toBeInfoRnDay) : 0; + const toBeRain = item.toBeInfoRnDay ? Number(item.toBeInfoRnDay) : 0; - if(toBeRain >= 3 && toBeRain < 10){ - rainDate[1] = Number(rainDate[1] || 0) + 1; - inflAmtCnt[1] = Number(inflAmtCnt[1] || 0) + 1; - inflAmnt[1] = Number(inflAmnt[1] || 0) + Number(item.inflAmnt || 0); + if (toBeRain >= 3 && toBeRain < 10) { + rainDate[1] = (rainDate[1] || 0) + 1; + inflAmnt[1] = (inflAmnt[1] || 0) + Number(item.inflAmnt || 0); fcltyCpct[1] = 0; inflAssctBfrWqltBodCnt[1] = Number(inflAssctBfrWqltBodCnt[1] || 0) + 1; inflAssctBfrWqltBod[1] = Number(inflAssctBfrWqltBod[1] || 0) + Number(item.inflAssctBfrWqltBod || 0); @@ -2134,7 +2109,7 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) inflAssctBfrWqltTpCnt[1] = Number(inflAssctBfrWqltTpCnt[1] || 0) + 1; inflAssctBfrWqltTp[1] = Number(inflAssctBfrWqltTp[1] || 0) + Number(item.inflAssctBfrWqltTp || 0); efflWqltBodCnt[1] = Number(efflWqltBodCnt[1] || 0) + 1; - efflWqltBod[1] = Number(efflWqltBod[1] || 0) + Number(item.efflWqltBod || 0); + efflWqltBod[1] = Number(efflWqltBod[1] || 0) + Number(item.efflWqltBod || 0); efflWqltTocCnt[1] = Number(efflWqltTocCnt[1] || 0) + 1; efflWqltToc[1] = Number(efflWqltToc[1] || 0) + Number(item.efflWqltToc || 0); efflWqltSsCnt[1] = Number(efflWqltSsCnt[1] || 0) + 1; @@ -2143,10 +2118,9 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) efflWqltTn[1] = Number(efflWqltTn[1] || 0) + Number(item.efflWqltTn || 0); efflWqltTpCnt[1] = Number(efflWqltTpCnt[1] || 0) + 1; efflWqltTp[1] = Number(efflWqltTp[1] || 0) + Number(item.efflWqltTp || 0); - }else if(toBeRain >= 10 && toBeRain < 20){ - rainDate[2] = Number(rainDate[2] || 0) + 1; - inflAmtCnt[2] = Number(inflAmtCnt[2] || 0) + 1; - inflAmnt[2] = Number(inflAmnt[2] || 0) + Number(item.inflAmnt || 0); + } else if (toBeRain >= 10 && toBeRain < 20) { + rainDate[2] = (rainDate[2] || 0) + 1; + inflAmnt[2] = (inflAmnt[2] || 0) + Number(item.inflAmnt || 0); fcltyCpct[2] = 0; inflAssctBfrWqltBodCnt[2] = Number(inflAssctBfrWqltBodCnt[2] || 0) + 1; inflAssctBfrWqltBod[2] = Number(inflAssctBfrWqltBod[2] || 0) + Number(item.inflAssctBfrWqltBod || 0); @@ -2168,10 +2142,9 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) efflWqltTn[2] = Number(efflWqltTn[2] || 0) + Number(item.efflWqltTn || 0); efflWqltTpCnt[2] = Number(efflWqltTpCnt[2] || 0) + 1; efflWqltTp[2] = Number(efflWqltTp[2] || 0) + Number(item.efflWqltTp || 0); - }else if(toBeRain >= 20 && toBeRain < 30){ - rainDate[3] = Number(rainDate[3] || 0) + 1; - inflAmtCnt[3] = Number(inflAmtCnt[3] || 0) + 1; - inflAmnt[3] = Number(inflAmnt[3] || 0) + Number(item.inflAmnt || 0); + } else if (toBeRain >= 20 && toBeRain < 30) { + rainDate[3] = (rainDate[3] || 0) + 1; + inflAmnt[3] = (inflAmnt[3] || 0) + Number(item.inflAmnt || 0); fcltyCpct[3] = 0; inflAssctBfrWqltBodCnt[3] = Number(inflAssctBfrWqltBodCnt[3] || 0) + 1; inflAssctBfrWqltBod[3] = Number(inflAssctBfrWqltBod[3] || 0) + Number(item.inflAssctBfrWqltBod || 0); @@ -2193,11 +2166,10 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) efflWqltTn[3] = Number(efflWqltTn[3] || 0) + Number(item.efflWqltTn || 0); efflWqltTpCnt[3] = Number(efflWqltTpCnt[3] || 0) + 1; efflWqltTp[3] = Number(efflWqltTp[3] || 0) + Number(item.efflWqltTp || 0); - }else if(toBeRain >= 30 && toBeRain < 40){ - rainDate[4] = Number(rainDate[4] || 0) + 1; - inflAmtCnt[4] = Number(inflAmtCnt[4] || 0) + 1; - inflAmnt[4] = Number(inflAmnt[4] || 0) + Number(item.inflAmnt || 0); - fcltyCpct[4] = item.inflAmnt&&Number(item.inflAmnt) !== 0 ? Number(Math.round(Number(item.inflAmnt) / Number(item.fctCpc) * 10) / 10) : 0; + } else if (toBeRain >= 30 && toBeRain < 40) { + rainDate[4] = (rainDate[4] || 0) + 1; + inflAmnt[4] = (inflAmnt[4] || 0) + Number(item.inflAmnt || 0); + fcltyCpct[4] = item.inflAmnt && Number(item.inflAmnt) !== 0 ? Number(Math.round(Number(item.inflAmnt) / Number(item.fctCpc) * 10) / 10) : 0; inflAssctBfrWqltBodCnt[4] = Number(inflAssctBfrWqltBodCnt[4] || 0) + 1; inflAssctBfrWqltBod[4] = Number(inflAssctBfrWqltBod[4] || 0) + Number(item.inflAssctBfrWqltBod || 0); inflAssctBfrWqltTocCnt[4] = Number(inflAssctBfrWqltTocCnt[4] || 0) + 1; @@ -2218,10 +2190,9 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) efflWqltTn[4] = Number(efflWqltTn[4] || 0) + Number(item.efflWqltTn || 0); efflWqltTpCnt[4] = Number(efflWqltTpCnt[4] || 0) + 1; efflWqltTp[4] = Number(efflWqltTp[4] || 0) + Number(item.efflWqltTp || 0); - }else if(toBeRain >= 40 && toBeRain < 50){ - rainDate[5] = Number(rainDate[5] || 0) + 1; - inflAmtCnt[5] = Number(inflAmtCnt[5] || 0) + 1; - inflAmnt[5] = Number(inflAmnt[5] || 0) + Number(item.inflAmnt || 0); + } else if (toBeRain >= 40 && toBeRain < 50) { + rainDate[5] = (rainDate[5] || 0) + 1; + inflAmnt[5] = (inflAmnt[5] || 0) + Number(item.inflAmnt || 0); fcltyCpct[5] = 0; inflAssctBfrWqltBodCnt[5] = Number(inflAssctBfrWqltBodCnt[5] || 0) + 1; inflAssctBfrWqltBod[5] = Number(inflAssctBfrWqltBod[5] || 0) + Number(item.inflAssctBfrWqltBod || 0); @@ -2243,10 +2214,9 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) efflWqltTn[5] = Number(efflWqltTn[5] || 0) + Number(item.efflWqltTn || 0); efflWqltTpCnt[5] = Number(efflWqltTpCnt[5] || 0) + 1; efflWqltTp[5] = Number(efflWqltTp[5] || 0) + Number(item.efflWqltTp || 0); - }else if(toBeRain >= 50 && toBeRain < 60){ - rainDate[6] = Number(rainDate[6] || 0) + 1; - inflAmtCnt[6] = Number(inflAmtCnt[6] || 0) + 1; - inflAmnt[6] = Number(inflAmnt[6] || 0) + Number(item.inflAmnt || 0); + } else if (toBeRain >= 50 && toBeRain < 60) { + rainDate[6] = (rainDate[6] || 0) + 1; + inflAmnt[6] = (inflAmnt[6] || 0) + Number(item.inflAmnt || 0); fcltyCpct[6] = 0; inflAssctBfrWqltBodCnt[6] = Number(inflAssctBfrWqltBodCnt[6] || 0) + 1; inflAssctBfrWqltBod[6] = Number(inflAssctBfrWqltBod[6] || 0) + Number(item.inflAssctBfrWqltBod || 0); @@ -2268,10 +2238,9 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) efflWqltTn[6] = Number(efflWqltTn[6] || 0) + Number(item.efflWqltTn || 0); efflWqltTpCnt[6] = Number(efflWqltTpCnt[6] || 0) + 1; efflWqltTp[6] = Number(efflWqltTp[6] || 0) + Number(item.efflWqltTp || 0); - }else if(toBeRain >= 60){ - rainDate[7] = Number(rainDate[7] || 0) + 1; - inflAmtCnt[7] = Number(inflAmtCnt[7] || 0) + 1; - inflAmnt[7] = Number(inflAmnt[7] || 0) + Number(item.inflAmnt || 0); + } else if (toBeRain >= 60) { + rainDate[7] = (rainDate[7] || 0) + 1; + inflAmnt[7] = (inflAmnt[7] || 0) + Number(item.inflAmnt || 0); fcltyCpct[7] = 0; inflAssctBfrWqltBodCnt[7] = Number(inflAssctBfrWqltBodCnt[7] || 0) + 1; inflAssctBfrWqltBod[7] = Number(inflAssctBfrWqltBod[7] || 0) + Number(item.inflAssctBfrWqltBod || 0); @@ -2295,18 +2264,52 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) efflWqltTp[7] = Number(efflWqltTp[7] || 0) + Number(item.efflWqltTp || 0); } }); - + + // 유량 = 순수하수 + 연계처리수 + 총인설비반류수 + const rainEffFlowList: any[] = CalcRainEffect(res2.data, 'RAIN_DAY'); + totInflAmnt = 0; inflAmnt.fill(0); rainDate.fill(0); + rainEffFlowList.forEach((item: any) => { // --일별 유량 start + const toBeRain = item.toBeInfoRnDay ? Number(item.toBeInfoRnDay) : 0; + const inflAmntOne = item.inflAmnt && item.inflAmnt !== 'x' ? Number(item.inflAmnt.toString().trim()) : 0; + const sumFlowOne = item.sumFlow && item.sumFlow !== 'x' ? Number(item.sumFlow.toString().trim()) : 0; + const returnAmntOne = item.returnAmnt && item.returnAmnt !== 'x' ? Number(item.returnAmnt.toString().trim()) : 0; + const inflowOne = inflAmntOne + sumFlowOne + returnAmntOne; + totInflAmnt = totInflAmnt + inflowOne; // 유량합계 + if (toBeRain >= 3 && toBeRain < 10) { + inflAmnt[1] = (inflAmnt[1] || 0) + inflowOne; + rainDate[1] = (rainDate[1] || 0) + 1; + } else if (toBeRain >= 10 && toBeRain < 20) { + inflAmnt[2] = (inflAmnt[2] || 0) + inflowOne; + rainDate[2] = (rainDate[2] || 0) + 1; + } else if (toBeRain >= 20 && toBeRain < 30) { + inflAmnt[3] = (inflAmnt[3] || 0) + inflowOne; + rainDate[3] = (rainDate[3] || 0) + 1; + } else if (toBeRain >= 30 && toBeRain < 40) { + inflAmnt[4] = (inflAmnt[4] || 0) + inflowOne; + rainDate[4] = (rainDate[4] || 0) + 1; + } else if (toBeRain >= 40 && toBeRain < 50) { + inflAmnt[5] = (inflAmnt[5] || 0) + inflowOne; + rainDate[5] = (rainDate[5] || 0) + 1; + } else if (toBeRain >= 50 && toBeRain < 60) { + inflAmnt[6] = (inflAmnt[6] || 0) + inflowOne; + rainDate[6] = (rainDate[6] || 0) + 1; + } else if (toBeRain >= 60) { + inflAmnt[7] = (inflAmnt[7] || 0) + inflowOne; + rainDate[7] = (rainDate[7] || 0) + 1; + } + }); // --일별 유량 end + // Process the data and create rain management list - if(res.data&&res.data.dsWqltObjs&&res.data.dsWqltObjs.length > 0){ + if (res.data && res.data.dsWqltObjs && res.data.dsWqltObjs.length > 0) { // 설계 유입수질 데이터 가져오기 - 0값이 있으면 이전 차수에서 가져오기 const getDesignWaterQualityData = (stepIndex: number, maxPreviousSteps: number = 3) => { for (let i = 0; i <= maxPreviousSteps && stepIndex - i >= 0; i++) { const currentStep = res.data.dsWqltObjs[stepIndex - i]; - if (currentStep && - currentStep.dsInflWqltBod > 0 && - currentStep.dsInflWqltCod > 0 && - currentStep.dsInflWqltSs > 0 && - currentStep.dsInflWqltTn > 0 && + if (currentStep && + currentStep.dsInflWqltBod > 0 && + currentStep.dsInflWqltCod > 0 && + currentStep.dsInflWqltSs > 0 && + currentStep.dsInflWqltTn > 0 && currentStep.dsInflWqltTp > 0) { return { dsInflWqltBod: currentStep.dsInflWqltBod, @@ -2329,23 +2332,23 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) const lastStepIndex = res.data.dsWqltObjs.length - 1; const designData = getDesignWaterQualityData(lastStepIndex); - + dsInflWqltBod[0] = designData.dsInflWqltBod; dsInflWqltCod[0] = designData.dsInflWqltCod; dsInflWqltSs[0] = designData.dsInflWqltSs; dsInflWqltTn[0] = designData.dsInflWqltTn; dsInflWqltTp[0] = designData.dsInflWqltTp; } - - rainDate[0] = (Number(rainDate[1]) || 0) + (Number(rainDate[2]) || 0) + (Number(rainDate[3]) || 0) + - (Number(rainDate[4]) || 0) + (Number(rainDate[5]) || 0) + (Number(rainDate[6]) || 0) + + + rainDate[0] = (Number(rainDate[1]) || 0) + (Number(rainDate[2]) || 0) + (Number(rainDate[3]) || 0) + + (Number(rainDate[4]) || 0) + (Number(rainDate[5]) || 0) + (Number(rainDate[6]) || 0) + (Number(rainDate[7]) || 0); - + // 강수비율 - for(let idx=0; idx<rainDate.length; idx++){ - if(Number(rainDate[idx]) > 0){ + for (let idx = 0; idx < rainDate.length; idx++) { + if (Number(rainDate[idx]) > 0) { rainRate[0] = '100'; - rainRate[idx] = (Math.round(Number(rainDate[idx])/Number(rainDate[0])*100*100)/100).toString(); + rainRate[idx] = (Math.round(Number(rainDate[idx]) / Number(rainDate[0]) * 100 * 100) / 100).toString(); } } @@ -2354,16 +2357,8 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) return count > 0 ? Number((value / count)) : 0; }; - // 데이터 있는 항목만 계산 - let dataIsCount = 0; - for(let idx=0; idx<inflAmnt.length; idx++){ - if(Number(inflAmnt[idx]) > 0){ - dataIsCount++; - } - } - - for(let idx=0; idx<inflAmnt.length; idx++){ - inflAmnt[idx] = calculateAverage(Number(inflAmnt[idx]), Number(inflAmtCnt[idx])); + for (let idx = 1; idx < inflAmnt.length; idx++) { + inflAmnt[idx] = rainDate[idx] ? Math.round((inflAmnt[idx] || 0) / (rainDate[idx] || 0)) : 0; inflAssctBfrWqltBod[idx] = calculateAverage(Number(inflAssctBfrWqltBod[idx]), Number(inflAssctBfrWqltBodCnt[idx])); inflAssctBfrWqltToc[idx] = calculateAverage(Number(inflAssctBfrWqltToc[idx]), Number(inflAssctBfrWqltTocCnt[idx])); inflAssctBfrWqltSs[idx] = calculateAverage(Number(inflAssctBfrWqltSs[idx]), Number(inflAssctBfrWqltSsCnt[idx])); @@ -2381,7 +2376,9 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) return Number((arr.slice(1).reduce((sum, val) => sum + Number(val), 0))); }; - inflAmnt[0] = calculateAverage(calculateTotal(inflAmnt), dataIsCount); + + // console.log(totInflAmnt); + inflAmnt[0] = Math.round((totInflAmnt / totItemCnt)); // calculateAverage(totInflAmnt, totItemCnt); fcltyCpct[0] = calculateTotal(fcltyCpct); inflAssctBfrWqltBod[0] = Number((Math.round((totInflAssctBfrWqltBod / totItemCnt) * 10) / 10).toFixed(1)); inflAssctBfrWqltToc[0] = Number((Math.round((totInflAssctBfrWqltToc / totItemCnt) * 10) / 10).toFixed(1)); @@ -2393,7 +2390,7 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) efflWqltSs[0] = Number((Math.round((totEfflWqltSs / totItemCnt) * 10) / 10).toFixed(1)); efflWqltTn[0] = Number((Math.round((totEfflWqltTn / totItemCnt) * 1000) / 1000).toFixed(3)); efflWqltTp[0] = Number((Math.round((totEfflWqltTp / totItemCnt) * 1000) / 1000).toFixed(3)); - + // 최종 리스트 const filteredData = itemList.map((item, index) => ({ rainItem: item, @@ -2411,15 +2408,15 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) inflAssctBfrWqltSs: inflAssctBfrWqltSs[index] ? (Math.round(inflAssctBfrWqltSs[index] * 10) / 10).toFixed(1) : '', inflAssctBfrWqltTn: inflAssctBfrWqltTn[index] ? (Math.round(inflAssctBfrWqltTn[index] * 1000) / 1000).toFixed(3) : '', inflAssctBfrWqltTp: inflAssctBfrWqltTp[index] ? (Math.round(inflAssctBfrWqltTp[index] * 1000) / 1000).toFixed(3) : '', - efflWqltBod: efflWqltBod[index] ? (Math.round(efflWqltBod[index] * 10) / 10).toFixed(1): '', + efflWqltBod: efflWqltBod[index] ? (Math.round(efflWqltBod[index] * 10) / 10).toFixed(1) : '', efflWqltToc: efflWqltToc[index] ? (Math.round(efflWqltToc[index] * 10) / 10).toFixed(1) : '', efflWqltSs: efflWqltSs[index] ? (Math.round(efflWqltSs[index] * 10) / 10).toFixed(1) : '', efflWqltTn: efflWqltTn[index] ? (Math.round(efflWqltTn[index] * 1000) / 1000).toFixed(3) : '', efflWqltTp: efflWqltTp[index] ? (Math.round(efflWqltTp[index] * 1000) / 1000).toFixed(3) : '' })); - - const excelList : RainManageExcelType[] = []; - if(isExcel){ + + const excelList: RainManageExcelType[] = []; + if (isExcel) { filteredData.forEach((item) => { excelList.push({ 강우량: item.rainItem, @@ -2430,7 +2427,7 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) 설계유입수질BOD: item.dsInflWqltBod ? (Math.round(Number(item.dsInflWqltBod) * 10) / 10).toFixed(1) : '', 설계유입수질TOC: item.dsInflWqltCod ? (Math.round(Number(item.dsInflWqltCod) * 10) / 10).toFixed(1) : '', 설계유입수질SS: item.dsInflWqltSs ? (Math.round(Number(item.dsInflWqltSs) * 10) / 10).toFixed(1) : '', - 설계유입수질TN: item.dsInflWqltTn ?(Math.round(Number(item.dsInflWqltTn) * 1000) / 1000).toFixed(3) : '', + 설계유입수질TN: item.dsInflWqltTn ? (Math.round(Number(item.dsInflWqltTn) * 1000) / 1000).toFixed(3) : '', 설계유입수질TP: item.dsInflWqltTp ? (Math.round(Number(item.dsInflWqltTp) * 1000) / 1000).toFixed(3) : '', 유입수질BOD: item.inflAssctBfrWqltBod ? (Math.round(Number(item.inflAssctBfrWqltBod) * 10) / 10).toFixed(1) : '', 유입수질TOC: item.inflAssctBfrWqltToc ? (Math.round(Number(item.inflAssctBfrWqltToc) * 10) / 10).toFixed(1) : '', @@ -2445,18 +2442,18 @@ export const dashBoardReducer:(set:(fn: (state: DashBoardType) => DashBoardType) }); }); } - + set((state: DashBoardType) => ({ ...state, RainManage: isExcel ? { ...state.RainManage, rainManageList: filteredData as any[], excelList: excelList, - isLoadingRainManage : false, + isLoadingRainManage: false, } : { ...state.RainManage, rainManageList: filteredData as any[], - isLoadingRainManage : false, + isLoadingRainManage: false, } })); }, diff --git a/apps/fo/app/store/facilityPrecipitation/index.tsx b/apps/fo/app/store/facilityPrecipitation/index.tsx index 6272ef55b..b18471eca 100644 --- a/apps/fo/app/store/facilityPrecipitation/index.tsx +++ b/apps/fo/app/store/facilityPrecipitation/index.tsx @@ -153,7 +153,9 @@ export const fetchFacilityRainDetailList = async (searchYear: string|undefined, } }; -const fetchFacilityPrecipitationList = async (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => { +const fetchFacilityPrecipitationList = async (pageNo: number, pageSize: number, searchYear: string, part?: string + , nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => { + const tailStr = '/'+pageNo + '/' + pageSize + '/' + searchYear + (part?'/' + part:'' ) + (nameCd?'/' + nameCd:'' ) + (fcltCpct?'/' + fcltCpct:'' ) + (operationValue?'/' + operationValue:'' ) + (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') ; const url = '/fclt/rain' + tailStr; const res = await getFetch('fo', url); diff --git a/apps/fo/app/store/facilityWaterQlty/index.tsx b/apps/fo/app/store/facilityWaterQlty/index.tsx index 0eafcfcd0..676c0624c 100644 --- a/apps/fo/app/store/facilityWaterQlty/index.tsx +++ b/apps/fo/app/store/facilityWaterQlty/index.tsx @@ -2,7 +2,7 @@ import DataForm, { roundNumber } from "@/app/components/common/DataForm"; import type { TableUpperProps } from "../dashboard"; import CalcRainEffect from "@/app/components/common/CalcRainEffect"; -import { COD_YEAR } from "../common"; +import { COD_YEAR } from "../common"; // 2021년 import CommaToNumber from "@/app/components/common/CommaToNumber"; export type { TableUpperProps }; @@ -28,17 +28,19 @@ export interface FacilityRdiiDetailProps { export interface FacilityWaterQltyProps { bscFctCd: string, bscFctNm: string, - msrDt: string - sidoNm: string, - sggNm: string, + msrDt: string, + sido: string, + gugun: string, + // sidoNm: string, + // sggNm: string, addr: string, fctCpc: number, inflAssctBfrWqltBod: string | '', inflAssctAftWqltBod: string | '', inflAssctBfrWqltToc: string | '', inflAssctAftWqltToc: string | '', - inflAssctBfrWqltCod : string | '', - inflAssctAftWqltCod : string | '', + inflAssctBfrWqltCod: string | '', + inflAssctAftWqltCod: string | '', inflAssctBfrWqltSs: string | '', inflAssctAftWqltSs: string | '', inflAssctBfrWqltTn: string | '', @@ -47,14 +49,14 @@ export interface FacilityWaterQltyProps { inflAssctAftWqltTp: string | '', efflWqltBod: string | '', efflWqltToc: string | '', - efflWqltCod : string | '', + efflWqltCod: string | '', efflWqltSs: string | '', efflWqltTn: string | '', efflWqltTp: string | '', - rgnNm:string|'', - rvbsn:string|'', - lgbsn:string|'', - mdmbsn:string|'', + rgnNm: string | '', + rvbsn: string | '', + lgbsn: string | '', + mdmbsn: string | '', inflAmnt: number | '', lclgvrnmntNm: string, infoRnDay: number | string | '', @@ -68,14 +70,14 @@ export interface FacilityWaterQltyProps { tpInflWqltTp: string | '', tpInflWqltEclgyTox: string | '', tpInflWqltClfgrpcnt: string | '', - noProcessInflAmnt : string | number | '', + noProcessInflAmnt: string | number | '', } export interface ExcelType { 시설코드: string, - 권역:string|'', - 단위유역:string|'', - 중권역:string|'', - 환경청:string|'', + 권역: string | '', + 단위유역: string | '', + 중권역: string | '', + 환경청: string | '', 시도: string, 시군구: string, 시설이름: string, @@ -146,74 +148,74 @@ const mainLabelArray: string[] = ['구분', '시도', '시군구', '용량별', // 시설수질리스트 헤더(행정별) const upHeadList1: TableUpperProps[] = [ - { id: "C", title: "행정명", colSpan: 2, row:0, widthPercent:10 }, - { id: "sidoNm", title: "시도", row: 1, widthPercent:5}, - { id: "sggNm", title: "시군구", row: 1, widthPercent:5 }, + { id: "C", title: "행정명", colSpan: 2, row: 0, widthPercent: 10 }, + { id: "sido", title: "시도", row: 1, widthPercent: 5 }, + { id: "gugun", title: "시군구", row: 1, widthPercent: 5 }, { id: "bscFctNm", title: "시설명", rowSpan: 2, row: 0, widthPercent: 10 }, { id: "addr", title: "주소", rowSpan: 2, row: 0 }, - { id: "fctCpc", title: "시설용량(m³/일)", align: "right", rowSpan: 2, row: 0, widthPercent:10 }, - { id: "A", title: "유입", colSpan: 5, row: 0, widthPercent:25 }, - { id: "B", title: "방류", colSpan: 5, row: 0, widthPercent:25 }, - { id: "inflAssctBfrWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe3", upSequnce: 1, align: "right", row: 1, widthPercent:5 }, - { id: "inflAssctBfrWqltToc", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe3", upSequnce: 2, align: "right", row: 1, widthPercent:5 }, - { id: "inflAssctBfrWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe3", upSequnce: 3, align: "right", row: 1, widthPercent:5 }, - { id: "inflAssctBfrWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe3", upSequnce: 4, align: "right", row: 1, widthPercent:5 }, - { id: "inflAssctBfrWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe3", upSequnce: 3, align: "right", row: 1, widthPercent:5 }, - { id: "efflWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe4", upSequnce: 1, align: "right", row: 1, widthPercent:5 }, - { id: "efflWqltToc", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe4", upSequnce: 2, align: "right", row: 1, widthPercent:5 }, - { id: "efflWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe4", upSequnce: 3, align: "right", row: 1, widthPercent:5 }, - { id: "efflWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe4", upSequnce: 4, align: "right", row: 1, widthPercent:5 }, - { id: "efflWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe4", upSequnce: 3, align: "right", row: 1, widthPercent:5 }, + { id: "fctCpc", title: "시설용량(m³/일)", align: "right", rowSpan: 2, row: 0, widthPercent: 10 }, + { id: "A", title: "유입", colSpan: 5, row: 0, widthPercent: 25 }, + { id: "B", title: "방류", colSpan: 5, row: 0, widthPercent: 25 }, + { id: "inflAssctBfrWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe3", upSequnce: 1, align: "right", row: 1, widthPercent: 5 }, + { id: "inflAssctBfrWqltToc", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe3", upSequnce: 2, align: "right", row: 1, widthPercent: 5 }, + { id: "inflAssctBfrWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe3", upSequnce: 3, align: "right", row: 1, widthPercent: 5 }, + { id: "inflAssctBfrWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe3", upSequnce: 4, align: "right", row: 1, widthPercent: 5 }, + { id: "inflAssctBfrWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe3", upSequnce: 3, align: "right", row: 1, widthPercent: 5 }, + { id: "efflWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe4", upSequnce: 1, align: "right", row: 1, widthPercent: 5 }, + { id: "efflWqltToc", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe4", upSequnce: 2, align: "right", row: 1, widthPercent: 5 }, + { id: "efflWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe4", upSequnce: 3, align: "right", row: 1, widthPercent: 5 }, + { id: "efflWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe4", upSequnce: 4, align: "right", row: 1, widthPercent: 5 }, + { id: "efflWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe4", upSequnce: 3, align: "right", row: 1, widthPercent: 5 }, ]; // 시설수질리스트 헤더(권역별) const upHeadList2: TableUpperProps[] = [ - { id: "C", title: "유역", colSpan: 3, row: 0, widthPercent:15 }, - { id: "rvbsn", title: "권역", upName: "upChangeRe1", upSequnce: 1, row:1, widthPercent:5 }, - { id: "lgbsn", title: "단위유역", upName: "upChangeRe1", upSequnce: 2, row:1, widthPercent:5 }, - { id: "mdmbsn", title: "중권역", upName: "upChangeRe1", upSequnce: 3, row:1, widthPercent:5 }, - { id: "bscFctNm", title: "시설명", row:0, rowSpan:2, widthPercent:10 }, - { id: "addr", title: "주소", row:0, rowSpan:2 }, - { id: "fctCpc", title: "시설용량(m³/L)", align: "right", row:0, rowSpan:2, widthPercent:8 }, - { id: "A", title: "유입", colSpan: 5, row: 0, widthPercent:25 }, - { id: "B", title: "방류", colSpan: 5, row: 0, widthPercent:25 }, - { id: "inflAssctBfrWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe3", upSequnce: 1, align: "right", row:1, widthPercent:5 }, - { id: "inflAssctBfrWqltToc", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe3", upSequnce: 2, align: "right", row:1, widthPercent:5 }, - { id: "inflAssctBfrWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe3", upSequnce: 3, align: "right", row:1, widthPercent:5 }, - { id: "inflAssctBfrWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe3", upSequnce: 2, align: "right", row:1, widthPercent:5 }, - { id: "inflAssctBfrWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe3", upSequnce: 3, align: "right", row:1, widthPercent:5 }, - { id: "efflWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe4", upSequnce: 1, align: "right", row:1, widthPercent:5 }, - { id: "efflWqltToc", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe4", upSequnce: 2, align: "right", row:1, widthPercent:5 }, - { id: "efflWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe4", upSequnce: 3, align: "right", row:1, widthPercent:5 }, - { id: "efflWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe4", upSequnce: 2, align: "right", row:1, widthPercent:5 }, - { id: "efflWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe4", upSequnce: 3, align: "right", row:1, widthPercent:5 }, + { id: "C", title: "유역", colSpan: 3, row: 0, widthPercent: 15 }, + { id: "rvbsn", title: "권역", upName: "upChangeRe1", upSequnce: 1, row: 1, widthPercent: 5 }, + { id: "lgbsn", title: "단위유역", upName: "upChangeRe1", upSequnce: 2, row: 1, widthPercent: 5 }, + { id: "mdmbsn", title: "중권역", upName: "upChangeRe1", upSequnce: 3, row: 1, widthPercent: 5 }, + { id: "bscFctNm", title: "시설명", row: 0, rowSpan: 2, widthPercent: 10 }, + { id: "addr", title: "주소", row: 0, rowSpan: 2 }, + { id: "fctCpc", title: "시설용량(m³/L)", align: "right", row: 0, rowSpan: 2, widthPercent: 8 }, + { id: "A", title: "유입", colSpan: 5, row: 0, widthPercent: 25 }, + { id: "B", title: "방류", colSpan: 5, row: 0, widthPercent: 25 }, + { id: "inflAssctBfrWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe3", upSequnce: 1, align: "right", row: 1, widthPercent: 5 }, + { id: "inflAssctBfrWqltToc", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe3", upSequnce: 2, align: "right", row: 1, widthPercent: 5 }, + { id: "inflAssctBfrWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe3", upSequnce: 3, align: "right", row: 1, widthPercent: 5 }, + { id: "inflAssctBfrWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe3", upSequnce: 2, align: "right", row: 1, widthPercent: 5 }, + { id: "inflAssctBfrWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe3", upSequnce: 3, align: "right", row: 1, widthPercent: 5 }, + { id: "efflWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe4", upSequnce: 1, align: "right", row: 1, widthPercent: 5 }, + { id: "efflWqltToc", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe4", upSequnce: 2, align: "right", row: 1, widthPercent: 5 }, + { id: "efflWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe4", upSequnce: 3, align: "right", row: 1, widthPercent: 5 }, + { id: "efflWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe4", upSequnce: 2, align: "right", row: 1, widthPercent: 5 }, + { id: "efflWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe4", upSequnce: 3, align: "right", row: 1, widthPercent: 5 }, ]; // 시설수질리스트 헤더(환경청별) const upHeadList3: TableUpperProps[] = [ { id: "C", title: "유역", colSpan: 3, row: 0, widthPercent: 15 }, - { id: "rvbsn", title: "권역", row:1, widthPercent: 5 }, - { id: "lgbsn", title: "단위유역", row:1, widthPercent: 5 }, - { id: "mdmbsn", title: "중권역", row:1, widthPercent: 5 }, - { id: "rgnNm", title: "환경청", row:0, rowSpan:2, widthPercent: 7 }, + { id: "rvbsn", title: "권역", row: 1, widthPercent: 5 }, + { id: "lgbsn", title: "단위유역", row: 1, widthPercent: 5 }, + { id: "mdmbsn", title: "중권역", row: 1, widthPercent: 5 }, + { id: "rgnNm", title: "환경청", row: 0, rowSpan: 2, widthPercent: 7 }, { id: "D", title: "행정명", colSpan: 2, row: 0, widthPercent: 10 }, - { id: "sidoNm", title: "시도", upName: "upChangeRe2", row:1, widthPercent: 5 }, - { id: "sggNm", title: "시군구", upName: "upChangeRe2", row:1, widthPercent: 5 }, - { id: "bscFctNm", title: "시설명", row:0, rowSpan:2, widthPercent: 8 }, - { id: "addr", title: "주소", row:0, rowSpan:2 }, - { id: "fctCpc", title: "시설용량(m³/L)", align: "right", row:0, rowSpan:2, widthPercent: 5 }, + { id: "sido", title: "시도", upName: "upChangeRe2", row: 1, widthPercent: 5 }, + { id: "gugun", title: "시군구", upName: "upChangeRe2", row: 1, widthPercent: 5 }, + { id: "bscFctNm", title: "시설명", row: 0, rowSpan: 2, widthPercent: 8 }, + { id: "addr", title: "주소", row: 0, rowSpan: 2 }, + { id: "fctCpc", title: "시설용량(m³/L)", align: "right", row: 0, rowSpan: 2, widthPercent: 5 }, { id: "A", title: "유입", colSpan: 5, row: 0, widthPercent: 20 }, { id: "B", title: "방류", colSpan: 5, row: 0, widthPercent: 20 }, - { id: "inflAssctBfrWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe3", upSequnce: 1, align: "right", row:1, widthPercent: 4 }, - { id: "inflAssctBfrWqltToc", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe3", upSequnce: 2, align: "right", row:1, widthPercent: 4 }, - { id: "inflAssctBfrWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe3", upSequnce: 3, align: "right", row:1, widthPercent: 4 }, - { id: "inflAssctBfrWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe3", upSequnce: 2, align: "right", row:1, widthPercent: 4 }, - { id: "inflAssctBfrWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe3", upSequnce: 3, align: "right", row:1, widthPercent: 4 }, - { id: "efflWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe4", upSequnce: 1, align: "right", row:1, widthPercent: 4 }, - { id: "efflWqltToc", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe4", upSequnce: 2, align: "right", row:1, widthPercent: 4 }, - { id: "efflWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe4", upSequnce: 3, align: "right", row:1, widthPercent: 4 }, - { id: "efflWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe4", upSequnce: 2, align: "right", row:1, widthPercent: 4 }, - { id: "efflWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe4", upSequnce: 3, align: "right", row:1, widthPercent: 4 }, + { id: "inflAssctBfrWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe3", upSequnce: 1, align: "right", row: 1, widthPercent: 4 }, + { id: "inflAssctBfrWqltToc", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe3", upSequnce: 2, align: "right", row: 1, widthPercent: 4 }, + { id: "inflAssctBfrWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe3", upSequnce: 3, align: "right", row: 1, widthPercent: 4 }, + { id: "inflAssctBfrWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe3", upSequnce: 2, align: "right", row: 1, widthPercent: 4 }, + { id: "inflAssctBfrWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe3", upSequnce: 3, align: "right", row: 1, widthPercent: 4 }, + { id: "efflWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe4", upSequnce: 1, align: "right", row: 1, widthPercent: 4 }, + { id: "efflWqltToc", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe4", upSequnce: 2, align: "right", row: 1, widthPercent: 4 }, + { id: "efflWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe4", upSequnce: 3, align: "right", row: 1, widthPercent: 4 }, + { id: "efflWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe4", upSequnce: 2, align: "right", row: 1, widthPercent: 4 }, + { id: "efflWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe4", upSequnce: 3, align: "right", row: 1, widthPercent: 4 }, ]; /** @@ -282,20 +284,24 @@ const upHeadListModalChongIn: TableUpperProps[] = [ ====================================*/ // 메인페이지 // 시설정보 수질 리스트 데이터 호출 -const fetchFacilityWaterQltyList = async (pageNo: number, pageSize: number, searchYear: string, partCd?: string|undefined, nameCd?: string|undefined, fcltCpct?: string|undefined, operationValue?: string|undefined, fcltyNm?: string | undefined): Promise<{ data: FacilityWaterQltyProps[], count: number }> => { +const fetchFacilityWaterQltyList = async ( + pageNo: number, pageSize: number, searchYear: string, partCd?: string | undefined + , nameCd?: string | undefined, fcltCpct?: string | undefined + , operationValue?: string | undefined, fcltyNm?: string | undefined +): Promise<{ data: FacilityWaterQltyProps[], count: number, status: number }> => { try { - const tailStr = partCd ? '/'+ partCd + '/' + nameCd + '/' + fcltCpct + '/' + operationValue + (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') : ''; + const tailStr = partCd ? '/' + partCd + '/' + nameCd + '/' + fcltCpct + '/' + operationValue + (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') : ''; const url = '/fclt/qlty/' + pageNo + '/' + pageSize + '/' + searchYear + tailStr; const res = await getFetch('fo', url); - return { data: res.data, count: res.count } + return { data: res.data, count: res.count, status: res.status }; } catch (error) { console.error('시설정보 수질현황 데이터를 가져오는데 실패했습니다:', error); - return { data: [], count: 0 }; + return { data: [], count: 0, status: 300 }; } } // 시설정보 수질 리스트 데이터 호출(모달-수질TMS) -const fetchFacilityWaterQltyTmsList = async (facilityCd: string|undefined) => { +const fetchFacilityWaterQltyTmsList = async (facilityCd: string | undefined) => { try { const res = await getFetch('wr', '/il/getTms/' + facilityCd); return { data: res.data, count: res.count }; @@ -306,7 +312,7 @@ const fetchFacilityWaterQltyTmsList = async (facilityCd: string|undefined) => { }; // 시설정보 수질 리스트 데이터 호출(모달-이력) -export const fetchFacilityWaterQltyListHist = async (searchYear: string|undefined, facilityCd: string|undefined): Promise<WaterQualityHistoryResponse | null> => { +export const fetchFacilityWaterQltyListHist = async (searchYear: string | undefined, facilityCd: string | undefined): Promise<WaterQualityHistoryResponse | null> => { try {// const res = await getFetch('fo', '/fclt/qlty/hist/' + searchYear + '/' + facilityCd); return res; @@ -316,9 +322,9 @@ export const fetchFacilityWaterQltyListHist = async (searchYear: string|undefine } }; // 시설정보 RDII 상세 [{BOD - inflAssctBfrWqltBod, 유입유량 inflAmnt, 강우량 infoRnDay, 측정일 msrDt}] -export const fetchFacilityRdiiDetailList = async (searchYear: string|undefined, swtrmtFcltCdList: string[]): Promise<FacilityRdiiDetailProps | null> => { +export const fetchFacilityRdiiDetailList = async (searchYear: string | undefined, swtrmtFcltCdList: string[]): Promise<FacilityRdiiDetailProps | null> => { try { - const res = await postFetch('fo', '/rdii/detail/'+searchYear, swtrmtFcltCdList); + const res = await postFetch('fo', '/rdii/detail/' + searchYear, swtrmtFcltCdList); return res; } catch (error) { console.error('시설정보 RDII 상세 데이터를 가져오는데 실패했습니다:', error); @@ -345,7 +351,7 @@ export type FacilityWaterQltyType = { FacilityWaterQltyModal: { // 처리시설 수질현황 upHeadListModalYear: TableUpperProps[]; - upHeadListModalYearCod : TableUpperProps[]; + upHeadListModalYearCod: TableUpperProps[]; facilityWaterQltyYearList: FacilityWaterQltyProps[]; upHeadListModalTms: TableUpperProps[]; facilityWaterQltyTmsList: FacilityWaterQltyTmsProps[]; @@ -432,7 +438,8 @@ const dataFormSetting = (item: FacilityWaterQltyProps) => { export type FacilityWaterQltyStore = FacilityWaterQltyType & FacilityWaterQltyActions; -export const facilityWaterQltyReducer = (set: (fn: (state: FacilityWaterQltyType) => FacilityWaterQltyType) => void): FacilityWaterQltyActions => { +type SetFunc = (fn: (state: FacilityWaterQltyType) => FacilityWaterQltyType) => void; +export const facilityWaterQltyReducer = (set: SetFunc): FacilityWaterQltyActions => { return { // 메인페이지 /** @@ -446,7 +453,9 @@ export const facilityWaterQltyReducer = (set: (fn: (state: FacilityWaterQltyType * @param operationValue : 운영상태코드 * @param fcltyNm : 시설명 */ - searchFacilityWaterQltyList: async (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => { + searchFacilityWaterQltyList: async (pageNo: number, pageSize: number, searchYear: string, part?: string + , nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => { + set((state: FacilityWaterQltyType) => ({ ...state, FacilityWaterQltyMain: { @@ -454,64 +463,90 @@ export const facilityWaterQltyReducer = (set: (fn: (state: FacilityWaterQltyType isLoading: true, }, })); - const res = await fetchFacilityWaterQltyList(pageNo, pageSize, searchYear, part, nameCd, fcltCpct, operationValue, fcltyNm); - const rtnData = res.data.map(item => { + + // const res = await fetchFacilityWaterQltyList(pageNo, pageSize, searchYear, part, nameCd, fcltCpct, operationValue, fcltyNm); + //--------- + const waterQltyList: any[] = []; + let recordCount = 0; + if (pageSize < 5000) { + const res = await fetchFacilityWaterQltyList(pageNo, pageSize, searchYear, part, nameCd, fcltCpct, operationValue, fcltyNm); + waterQltyList.push(...res.data); + recordCount = res.count; + } else { + for (let idx = 0; idx < pageSize; idx += 100) { + const tmpPageNo = Math.floor(idx / 100) + 1; + const tmpPageSize = Math.min(100, pageSize - idx); + const result = await fetchFacilityWaterQltyList(tmpPageNo, tmpPageSize, searchYear, part, nameCd, fcltCpct, operationValue, fcltyNm); + if (result?.status === 200 && Array.isArray(result?.data)) { + waterQltyList.push(...result.data); + } + recordCount = result?.count || 0; + if (result?.count < tmpPageNo * tmpPageSize) { + break; // 더 이상 데이터가 없으면 중지 + } + } + } + //------- + + // const rtnData = res.data.map(item => { + const rtnData = waterQltyList.map(item => { dataFormSetting(item) - if(item.inflAssctBfrWqltTn) { - item.inflAssctBfrWqltTn = Number(Math.round(Number(item.inflAssctBfrWqltTn) * 1000)/1000).toFixed(3); + if (item.inflAssctBfrWqltTn) { + item.inflAssctBfrWqltTn = Number(Math.round(Number(item.inflAssctBfrWqltTn) * 1000) / 1000).toFixed(3); } - if(item.efflWqltTn){ - item.efflWqltTn = Number(Math.round(Number(item.efflWqltTn) * 1000)/1000).toFixed(3); + if (item.efflWqltTn) { + item.efflWqltTn = Number(Math.round(Number(item.efflWqltTn) * 1000) / 1000).toFixed(3); } - if(item.inflAssctBfrWqltTp){ - item.inflAssctBfrWqltTp = Number(Math.round(Number(item.inflAssctBfrWqltTp) * 1000)/1000).toFixed(3); + if (item.inflAssctBfrWqltTp) { + item.inflAssctBfrWqltTp = Number(Math.round(Number(item.inflAssctBfrWqltTp) * 1000) / 1000).toFixed(3); } - if(item.efflWqltTp){ - item.efflWqltTp = Number(Math.round(Number(item.efflWqltTp) * 1000)/1000).toFixed(3); + if (item.efflWqltTp) { + item.efflWqltTp = Number(Math.round(Number(item.efflWqltTp) * 1000) / 1000).toFixed(3); } - if(item.inflAssctBfrWqltToc){ - item.inflAssctBfrWqltToc = Number(Math.round(Number(item.inflAssctBfrWqltToc) * 10)/10).toFixed(1); + if (item.inflAssctBfrWqltToc) { + item.inflAssctBfrWqltToc = Number(Math.round(Number(item.inflAssctBfrWqltToc) * 10) / 10).toFixed(1); } - if(item.efflWqltToc){ - item.efflWqltToc = Number(Math.round(Number(item.efflWqltToc) * 10)/10).toFixed(1); + if (item.efflWqltToc) { + item.efflWqltToc = Number(Math.round(Number(item.efflWqltToc) * 10) / 10).toFixed(1); } - if(item.inflAssctBfrWqltBod){ - item.inflAssctBfrWqltBod = Number(Math.round(Number(item.inflAssctBfrWqltBod) * 10)/10).toFixed(1); + if (item.inflAssctBfrWqltBod) { + item.inflAssctBfrWqltBod = Number(Math.round(Number(item.inflAssctBfrWqltBod) * 10) / 10).toFixed(1); } - if(item.efflWqltBod){ - item.efflWqltBod = Number(Math.round(Number(item.efflWqltBod) * 10)/10).toFixed(1); + if (item.efflWqltBod) { + item.efflWqltBod = Number(Math.round(Number(item.efflWqltBod) * 10) / 10).toFixed(1); } - if(item.inflAssctBfrWqltSs){ - item.inflAssctBfrWqltSs = Number(Math.round(Number(item.inflAssctBfrWqltSs) * 10)/10).toFixed(1); + if (item.inflAssctBfrWqltSs) { + item.inflAssctBfrWqltSs = Number(Math.round(Number(item.inflAssctBfrWqltSs) * 10) / 10).toFixed(1); } - if(item.efflWqltSs){ - item.efflWqltSs = Number(Math.round(Number(item.efflWqltSs) * 10)/10).toFixed(1); + if (item.efflWqltSs) { + item.efflWqltSs = Number(Math.round(Number(item.efflWqltSs) * 10) / 10).toFixed(1); } - if(item.inflAssctBfrWqltToc){ - item.inflAssctBfrWqltToc = Number(Math.round(Number(item.inflAssctBfrWqltToc) * 10)/10).toFixed(1); + if (item.inflAssctBfrWqltToc) { + item.inflAssctBfrWqltToc = Number(Math.round(Number(item.inflAssctBfrWqltToc) * 10) / 10).toFixed(1); } - if(item.efflWqltToc){ - item.efflWqltToc = Number(Math.round(Number(item.efflWqltToc) * 10)/10).toFixed(1); + if (item.efflWqltToc) { + item.efflWqltToc = Number(Math.round(Number(item.efflWqltToc) * 10) / 10).toFixed(1); } return item; }); const excelDownList: ExcelType[] = []; - if(pageSize>5000){ - res.data.forEach(item => { + if (pageSize > 5000) { + // res.data.forEach(item => { + waterQltyList.forEach(item => { dataFormSetting(item) item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, '')); - item.sidoNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || ''; - item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || ''; - item.inflAssctBfrWqltToc = Number(searchYear)<COD_YEAR? "("+item.inflAssctBfrWqltToc+")" : item.inflAssctBfrWqltToc; - item.efflWqltToc = Number(searchYear)<COD_YEAR? "("+item.efflWqltToc+")" : item.efflWqltToc; + // item.sidoNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || ''; + // item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || ''; + item.inflAssctBfrWqltToc = Number(searchYear) < COD_YEAR ? "(" + item.inflAssctBfrWqltToc + ")" : item.inflAssctBfrWqltToc; + item.efflWqltToc = Number(searchYear) < COD_YEAR ? "(" + item.efflWqltToc + ")" : item.efflWqltToc; excelDownList.push({ 시설코드: item.bscFctCd, 권역: item.rvbsn, 단위유역: item.lgbsn, 중권역: item.mdmbsn, 환경청: item.rgnNm, - 시도: item.sidoNm, - 시군구: item.sggNm, + 시도: item.sido, + 시군구: item.gugun, 시설이름: item.bscFctNm, 주소: item.addr, "시설용량(m³/일)": CommaToNumber(item.fctCpc), @@ -527,27 +562,28 @@ export const facilityWaterQltyReducer = (set: (fn: (state: FacilityWaterQltyType "유출TP(mg/L)": CommaToNumber(item.efflWqltTp), }); }); - }else{ - res.data.forEach((item: FacilityWaterQltyProps) => { + } else { + // res.data.forEach((item: FacilityWaterQltyProps) => { + waterQltyList.forEach((item: FacilityWaterQltyProps) => { dataFormSetting(item) item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, '')); - item.sidoNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || ''; - item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || ''; - item.inflAssctBfrWqltToc = Number(searchYear)<COD_YEAR? "("+item.inflAssctBfrWqltToc+")" : item.inflAssctBfrWqltToc; - item.efflWqltToc = Number(searchYear)<COD_YEAR? "("+item.efflWqltToc+")" : item.efflWqltToc; + // item.sidoNm = item.; + // item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || ''; + item.inflAssctBfrWqltToc = Number(searchYear) < COD_YEAR ? "(" + item.inflAssctBfrWqltToc + ")" : item.inflAssctBfrWqltToc; + item.efflWqltToc = Number(searchYear) < COD_YEAR ? "(" + item.efflWqltToc + ")" : item.efflWqltToc; }); } set((state: FacilityWaterQltyType) => ({ ...state, - FacilityWaterQltyMain: pageSize<5000?{ + FacilityWaterQltyMain: pageSize < 5000 ? { ...state.FacilityWaterQltyMain, facilityWaterQltyList: rtnData, - facilityWaterQltyCount: res.count, - isLoading : false, - }:{ + facilityWaterQltyCount: recordCount, + isLoading: false, + } : { ...state.FacilityWaterQltyMain, excelList: excelDownList, - isLoading : false, + isLoading: false, }, })) }, @@ -555,10 +591,10 @@ export const facilityWaterQltyReducer = (set: (fn: (state: FacilityWaterQltyType searchFacilityWaterQltyListHist: async (searchYear: string, facilityCd: string) => { const res = await fetchFacilityWaterQltyListHist(searchYear, facilityCd); const res1 = await fetchFacilityWaterQltyTmsList(facilityCd); - + let rtnMsrmtDate = ''; const rtnTmsList: FacilityWaterQltyTmsProps[] = []; - if(res1.data){ + if (res1.data) { rtnTmsList.push({ part: '평균', toc: res1.data.tocAvgVl, @@ -566,7 +602,6 @@ export const facilityWaterQltyReducer = (set: (fn: (state: FacilityWaterQltyType tn: res1.data.tnAvgVl, tp: res1.data.tpAvgVl, }); - rtnTmsList.push({ part: '최대', toc: res1.data.tocMaxVl, @@ -574,7 +609,6 @@ export const facilityWaterQltyReducer = (set: (fn: (state: FacilityWaterQltyType tn: res1.data.tnMaxVl, tp: res1.data.tpMaxVl, }); - rtnTmsList.push({ part: '최소', toc: res1.data.tocMinVl, @@ -584,42 +618,21 @@ export const facilityWaterQltyReducer = (set: (fn: (state: FacilityWaterQltyType }); rtnMsrmtDate = res1.data.msrmtDate; - }else{ - rtnTmsList.push({ - part: '평균', - toc: 0, - ss: 0, - tn: 0, - tp: 0, - }); - - rtnTmsList.push({ - part: '최대', - toc: 0, - ss: 0, - tn: 0, - tp: 0, - }); - - rtnTmsList.push({ - part: '최소', - toc: 0, - ss: 0, - tn: 0, - tp: 0, - }); - + } else { + rtnTmsList.push({ part: '평균', toc: 0, ss: 0, tn: 0, tp: 0, }); + rtnTmsList.push({ part: '최대', toc: 0, ss: 0, tn: 0, tp: 0, }); + rtnTmsList.push({ part: '최소', toc: 0, ss: 0, tn: 0, tp: 0, }); rtnMsrmtDate = ''; } - - if(Array.isArray(res) || !res || !('data' in res) || !res.data || !res.data.wqltObjs || res.data.wqltObjs.length === 0) { + + if (Array.isArray(res) || !res || !('data' in res) || !res.data || !res.data.wqltObjs || res.data.wqltObjs.length === 0) { set((state: FacilityWaterQltyType) => ({ ...state, FacilityWaterQltyModal: { ...state.FacilityWaterQltyModal, facilityWaterQltyYearList: [], - tmsList : rtnTmsList, - msrmtDate : rtnMsrmtDate + tmsList: rtnTmsList, + msrmtDate: rtnMsrmtDate } })) return; @@ -627,19 +640,17 @@ export const facilityWaterQltyReducer = (set: (fn: (state: FacilityWaterQltyType // facilityPrecipitation의 rainEffectDay 계산 로직을 적용 const data = CalcRainEffect(res.data.wqltObjs); - + /** * [(강우시 일별유입유량*강우시 일별유입BOD)-(청천시 연평균유입유량*청천시 연평균유입BOD)]​ /강우시 일별유입BOD) */ - //let rainInflAmnt = 0; let sunnyInflAmnt = 0; let sunnyBod = 0; - //let rainDay = 0; let sunnyDay = 0; data.forEach(item => { - if(item.rainEffectDay == 1){ + if (item.rainEffectDay == 1) { //rainDay += 1; //rainInflAmnt += Number(item.inflAmnt); } else { @@ -650,69 +661,69 @@ export const facilityWaterQltyReducer = (set: (fn: (state: FacilityWaterQltyType }); //const yearRainAmnt = rainInflAmnt===0?0:rainInflAmnt / rainDay; - const yearSunnyAmnt = sunnyInflAmnt===0?0:sunnyInflAmnt / sunnyDay; - const yearSunnyBod = sunnyBod===0?0:sunnyBod / sunnyDay; + const yearSunnyAmnt = sunnyInflAmnt === 0 ? 0 : sunnyInflAmnt / sunnyDay; + const yearSunnyBod = sunnyBod === 0 ? 0 : sunnyBod / sunnyDay; // const rtnData = data.map((item: FacilityWaterQltyProps) => { dataFormSetting(item) - if(item.inflAssctBfrWqltTn) { - item.inflAssctBfrWqltTn = Number(Math.round(Number(item.inflAssctBfrWqltTn) * 1000)/1000).toFixed(3); + if (item.inflAssctBfrWqltTn) { + item.inflAssctBfrWqltTn = Number(Math.round(Number(item.inflAssctBfrWqltTn) * 1000) / 1000).toFixed(3); } - if(item.efflWqltTn){ - item.efflWqltTn = Number(Math.round(Number(item.efflWqltTn) * 1000)/1000).toFixed(3); + if (item.efflWqltTn) { + item.efflWqltTn = Number(Math.round(Number(item.efflWqltTn) * 1000) / 1000).toFixed(3); } - if(item.inflAssctBfrWqltTp){ - item.inflAssctBfrWqltTp = Number(Math.round(Number(item.inflAssctBfrWqltTp) * 1000)/1000).toFixed(3); + if (item.inflAssctBfrWqltTp) { + item.inflAssctBfrWqltTp = Number(Math.round(Number(item.inflAssctBfrWqltTp) * 1000) / 1000).toFixed(3); } - if(item.efflWqltTp){ - item.efflWqltTp = Number(Math.round(Number(item.efflWqltTp) * 1000)/1000).toFixed(3); + if (item.efflWqltTp) { + item.efflWqltTp = Number(Math.round(Number(item.efflWqltTp) * 1000) / 1000).toFixed(3); } - if(item.inflAssctBfrWqltToc){ - item.inflAssctBfrWqltToc = Number(Math.round(Number(item.inflAssctBfrWqltToc) * 10)/10).toFixed(1); + if (item.inflAssctBfrWqltToc) { + item.inflAssctBfrWqltToc = Number(Math.round(Number(item.inflAssctBfrWqltToc) * 10) / 10).toFixed(1); } - if(item.efflWqltToc){ - item.efflWqltToc = Number(Math.round(Number(item.efflWqltToc) * 10)/10).toFixed(1); + if (item.efflWqltToc) { + item.efflWqltToc = Number(Math.round(Number(item.efflWqltToc) * 10) / 10).toFixed(1); } - if(item.inflAssctBfrWqltCod){ - item.inflAssctBfrWqltCod = "("+Number(Math.round(Number(item.inflAssctBfrWqltCod) * 10)/10).toFixed(1)+")"; + if (item.inflAssctBfrWqltCod) { + item.inflAssctBfrWqltCod = "(" + Number(Math.round(Number(item.inflAssctBfrWqltCod) * 10) / 10).toFixed(1) + ")"; } - if(item.efflWqltCod){ - item.efflWqltCod = "("+Number(Math.round(Number(item.efflWqltCod) * 10)/10).toFixed(1)+")"; + if (item.efflWqltCod) { + item.efflWqltCod = "(" + Number(Math.round(Number(item.efflWqltCod) * 10) / 10).toFixed(1) + ")"; } - if(item.inflAssctBfrWqltBod){ - item.inflAssctBfrWqltBod = Number(Math.round(Number(item.inflAssctBfrWqltBod) * 10)/10).toFixed(1); + if (item.inflAssctBfrWqltBod) { + item.inflAssctBfrWqltBod = Number(Math.round(Number(item.inflAssctBfrWqltBod) * 10) / 10).toFixed(1); } - if(item.efflWqltBod){ - item.efflWqltBod = Number(Math.round(Number(item.efflWqltBod) * 10)/10).toFixed(1); + if (item.efflWqltBod) { + item.efflWqltBod = Number(Math.round(Number(item.efflWqltBod) * 10) / 10).toFixed(1); } - if(item.inflAssctBfrWqltSs){ - item.inflAssctBfrWqltSs = Number(Math.round(Number(item.inflAssctBfrWqltSs) * 10)/10).toFixed(1); + if (item.inflAssctBfrWqltSs) { + item.inflAssctBfrWqltSs = Number(Math.round(Number(item.inflAssctBfrWqltSs) * 10) / 10).toFixed(1); } - if(item.efflWqltSs){ - item.efflWqltSs = Number(Math.round(Number(item.efflWqltSs) * 10)/10).toFixed(1); + if (item.efflWqltSs) { + item.efflWqltSs = Number(Math.round(Number(item.efflWqltSs) * 10) / 10).toFixed(1); } - if(item.inflAssctBfrWqltToc){ - item.inflAssctBfrWqltToc = Number(Math.round(Number(item.inflAssctBfrWqltToc) * 10)/10).toFixed(1); + if (item.inflAssctBfrWqltToc) { + item.inflAssctBfrWqltToc = Number(Math.round(Number(item.inflAssctBfrWqltToc) * 10) / 10).toFixed(1); } - if(item.efflWqltToc){ - item.efflWqltToc = Number(Math.round(Number(item.efflWqltToc) * 10)/10).toFixed(1); + if (item.efflWqltToc) { + item.efflWqltToc = Number(Math.round(Number(item.efflWqltToc) * 10) / 10).toFixed(1); } - if(item.tpInflWqltBod){ - item.tpInflWqltBod = item.tpInflWqltBod?Number(Math.round(Number(item.tpInflWqltBod) * 10)/10).toFixed(1):Number(item.tpInflWqltBod)===0?'0.0':''; - }else{ + if (item.tpInflWqltBod) { // 총인 유입수질 BOD(mg/L) + item.tpInflWqltBod = item.tpInflWqltBod ? Number(Math.round(Number(item.tpInflWqltBod) * 10) / 10).toFixed(1) : Number(item.tpInflWqltBod) === 0 ? '0.0' : ''; + } else { item.tpInflWqltBod = ''; } - if(item.tpInflWqltTp){ - item.tpInflWqltTp = item.tpInflWqltTp?Number(Math.round(Number(item.tpInflWqltTp) * 1000)/1000).toFixed(3): Number(item.tpInflWqltTp)===0?'0.000':''; - }else{ + if (item.tpInflWqltTp) { // 총인 유입수질 TP(mg/L) + item.tpInflWqltTp = item.tpInflWqltTp ? Number(Math.round(Number(item.tpInflWqltTp) * 1000) / 1000).toFixed(3) : Number(item.tpInflWqltTp) === 0 ? '0.000' : ''; + } else { item.tpInflWqltTp = ''; } const dayBod = Number(item.inflAssctBfrWqltBod); const noProcessInflAmnt = roundNumber(((Number(item.inflAmnt) * dayBod) - (yearSunnyAmnt * yearSunnyBod)) / dayBod, 0) - item.noProcessInflAmnt = noProcessInflAmnt && Number(noProcessInflAmnt.replaceAll(',','')) > 0 ? noProcessInflAmnt : ''; + item.noProcessInflAmnt = noProcessInflAmnt && Number(noProcessInflAmnt.replaceAll(',', '')) > 0 ? noProcessInflAmnt : ''; return item; }) @@ -721,8 +732,8 @@ export const facilityWaterQltyReducer = (set: (fn: (state: FacilityWaterQltyType FacilityWaterQltyModal: { ...state.FacilityWaterQltyModal, facilityWaterQltyYearList: rtnData, - tmsList : rtnTmsList, - msrmtDate : rtnMsrmtDate + tmsList: rtnTmsList, + msrmtDate: rtnMsrmtDate } })) }, diff --git a/apps/fo/app/store/flowRate/index.tsx b/apps/fo/app/store/flowRate/index.tsx index 66d639fb8..b097fc234 100644 --- a/apps/fo/app/store/flowRate/index.tsx +++ b/apps/fo/app/store/flowRate/index.tsx @@ -1,6 +1,6 @@ import { getFetch } from "@/app/components/api"; import type { TableUpperProps } from "../dashboard"; -import DataForm, { roundNumber } from "@/app/components/common/DataForm"; +import DataForm from "@/app/components/common/DataForm"; import CalcRainEffect from "@/app/components/common/CalcRainEffect"; import { fetchFacilityWaterQltyListHist } from "../facilityWaterQlty"; import CommaToNumber from "@/app/components/common/CommaToNumber"; @@ -12,34 +12,34 @@ import CommaToNumber from "@/app/components/common/CommaToNumber"; /** * (1) 메인페이지 */ -const FlowRateSearchLabelArray:string[] = ['구분', '시도', '시군구', '용량별', '시설명', '조회기간', '시설상태', '권역', '단위유역', '중권역', '환경청별']; +const FlowRateSearchLabelArray: string[] = ['구분', '시도', '시군구', '용량별', '시설명', '조회기간', '시설상태', '권역', '단위유역', '중권역', '환경청별']; export type flowRateListProps = { - bscFctCd : string, - sido : string, - gugun : string, - bscFctNm : string, - daddr : string, - fctCpc : number, - lclgvrnmntNm : string, - totInflAmnt : number | '', - inflAmnt : number | '', - sumFlow : number | '', - returnAmnt : number | '', - efflAmnt : number | '', - rgnNm : string, - rvbsn : string, - lgbsn : string, - mdmbsn : string, - infoRnDay : number | '', + bscFctCd: string, + sido: string, + gugun: string, + bscFctNm: string, + daddr: string, + fctCpc: number, + lclgvrnmntNm: string, + totInflAmnt: number | '', + inflAmnt: number | '', + sumFlow: number | '', + returnAmnt: number | '', + efflAmnt: number | '', + rgnNm: string, + rvbsn: string, + lgbsn: string, + mdmbsn: string, + infoRnDay: number | '', } export interface ExcelType { 시설코드: string, - 권역:string|'', - 단위유역:string|'', - 중권역:string|'', - 환경청:string|'', + 권역: string | '', + 단위유역: string | '', + 중권역: string | '', + 환경청: string | '', 시도: string, 시군구: string, 시설이름: string, @@ -66,105 +66,105 @@ export type modalExcelType = { * (2) 모달 */ export interface FlowRateModalInterface { - msrDt : string, - totInflAmnt : number | '', - inflAmnt : number | '', - sumFlow : number | '', - returnAmnt : number | '', - efflAmnt : number | '', - infoRnDay : number | string | '', - rainEffectDay : number | string | '' , - noProcessInflAmnt : number | string | '' + msrDt: string, + totInflAmnt: number | '', + inflAmnt: number | '', + sumFlow: number | '', + returnAmnt: number | '', + efflAmnt: number | '', + infoRnDay: number | string | '', + rainEffectDay: number | string | '', + noProcessInflAmnt: number | string | '' } export type FlowRateModalProps = { - msrDt : string, - totInflAmnt : number, - inflAmnt : number, - sumFlow : number, - returnAmnt : number, - efflAmnt : number, - infoRnDay : number | string | '', - rainEffectDay : number | string | '', - noProcessInflAmnt : number | string | '' + msrDt: string, + totInflAmnt: number, + inflAmnt: number, + sumFlow: number, + returnAmnt: number, + efflAmnt: number, + infoRnDay: number | string | '', + rainEffectDay: number | string | '', + noProcessInflAmnt: number | string | '' } /**==================================== * Store - data ====================================*/ - /** - * (1) 메인페이지 - */ +/** +* (1) 메인페이지 +*/ // 리스트 (행정별) -const upHeadList1:TableUpperProps[] = [ +const upHeadList1: TableUpperProps[] = [ { id: "A", title: "행정명", row: 0, colSpan: 2, widthPercent: 16 }, - { id: "sido", title: "시도", upName:"upChangeRe", row:1, widthPercent: 8 }, - { id: "gugun", title: "시군구", upName:"upChangeRe", row:1, widthPercent: 8 }, - { id: "bscFctNm", title: "시설명", row: 0, rowSpan: 2, widthPercent: 10}, + { id: "sido", title: "시도", upName: "upChangeRe", row: 1, widthPercent: 8 }, + { id: "gugun", title: "시군구", upName: "upChangeRe", row: 1, widthPercent: 8 }, + { id: "bscFctNm", title: "시설명", row: 0, rowSpan: 2, widthPercent: 10 }, { id: "daddr", title: "주소", row: 0, rowSpan: 2 }, - { id: "fctCpc", title: "시설용량\n(m³/일)", align:"right", row: 0, rowSpan: 2, widthPercent: 7 }, - { id: "totInflAmnt", title: "총 유입량\n(m³/일)", align:"right", row: 0, rowSpan: 2, widthPercent: 7 }, - { id: "inflAmnt", title: "하수유입량\n(m³/일)", align:"right", row: 0, rowSpan: 2, widthPercent: 7 }, - { id: "sumFlow", title: '연계처리량\n(m³/일)', align:"right", row: 0, rowSpan: 2, widthPercent: 7 }, - { id: "returnAmnt", title: '총인 반류수량\n(m³/일)', align:"right", row: 0, rowSpan: 2, widthPercent: 7 }, - { id: "efflAmnt", title: '방류량\n(m³/일)', align:"right", row: 0, rowSpan: 2, widthPercent: 7 } + { id: "fctCpc", title: "시설용량\n(m³/일)", align: "right", row: 0, rowSpan: 2, widthPercent: 7 }, + { id: "totInflAmnt", title: "총 유입량\n(m³/일)", align: "right", row: 0, rowSpan: 2, widthPercent: 7 }, + { id: "inflAmnt", title: "하수유입량\n(m³/일)", align: "right", row: 0, rowSpan: 2, widthPercent: 7 }, + { id: "sumFlow", title: '연계처리량\n(m³/일)', align: "right", row: 0, rowSpan: 2, widthPercent: 7 }, + { id: "returnAmnt", title: '총인 반류수량\n(m³/일)', align: "right", row: 0, rowSpan: 2, widthPercent: 7 }, + { id: "efflAmnt", title: '방류량\n(m³/일)', align: "right", row: 0, rowSpan: 2, widthPercent: 7 } ]; // 리스트 (권역별) -const upHeadList2:TableUpperProps[] = [ +const upHeadList2: TableUpperProps[] = [ { id: "A", title: "유역", row: 0, colSpan: 3, widthPercent: 24 }, - { id: "rvbsn", title: "권역", upName:"upChangeRe", row:1, widthPercent: 8 }, - { id: "lgbsn", title: "단위유역", upName:"upChangeRe", row:1, widthPercent: 8 }, - { id: "mdmbsn", title: "중권역", upName:"upChangeRe", row:1, widthPercent: 8 }, + { id: "rvbsn", title: "권역", upName: "upChangeRe", row: 1, widthPercent: 8 }, + { id: "lgbsn", title: "단위유역", upName: "upChangeRe", row: 1, widthPercent: 8 }, + { id: "mdmbsn", title: "중권역", upName: "upChangeRe", row: 1, widthPercent: 8 }, { id: "bscFctNm", title: "시설명", row: 0, rowSpan: 2, widthPercent: 10 }, { id: "daddr", title: "주소", row: 0, rowSpan: 2 }, - { id: "fctCpc", title: "시설용량\n(m³/일)", align:"right", row: 0, rowSpan: 2, widthPercent: 10 }, - { id: "totInflAmnt", title: "총 유입량\n(m³/일)", align:"right", row: 0, rowSpan: 2, widthPercent: 7 }, - { id: "inflAmnt", title: "하수유입량\n(m³/일)", align:"right", row: 0, rowSpan: 2, widthPercent: 7 }, - { id: "sumFlow", title: '연계처리량\n(m³/일)', align:"right", row: 0, rowSpan: 2, widthPercent: 7 }, - { id: "returnAmnt", title: '총인 반류수량\n(m³/일)', align:"right", row: 0, rowSpan: 2, widthPercent: 7 }, - { id: "efflAmnt", title: '방류량\n(m³/일)', align:"right", row: 0, rowSpan: 2, widthPercent: 7 } + { id: "fctCpc", title: "시설용량\n(m³/일)", align: "right", row: 0, rowSpan: 2, widthPercent: 10 }, + { id: "totInflAmnt", title: "총 유입량\n(m³/일)", align: "right", row: 0, rowSpan: 2, widthPercent: 7 }, + { id: "inflAmnt", title: "하수유입량\n(m³/일)", align: "right", row: 0, rowSpan: 2, widthPercent: 7 }, + { id: "sumFlow", title: '연계처리량\n(m³/일)', align: "right", row: 0, rowSpan: 2, widthPercent: 7 }, + { id: "returnAmnt", title: '총인 반류수량\n(m³/일)', align: "right", row: 0, rowSpan: 2, widthPercent: 7 }, + { id: "efflAmnt", title: '방류량\n(m³/일)', align: "right", row: 0, rowSpan: 2, widthPercent: 7 } ]; // 리스트 (환경청별별) -const upHeadList3:TableUpperProps[] = [ +const upHeadList3: TableUpperProps[] = [ { id: "A", title: "유역", row: 0, colSpan: 3, widthPercent: 15 }, - { id: "rvbsn", title: "권역", upName:"upChangeRe1", row:1, widthPercent: 5 }, - { id: "lgbsn", title: "단위유역", upName:"upChangeRe1", row:1, widthPercent: 5 }, - { id: "mdmbsn", title: "중권역", upName:"upChangeRe1", row:1, widthPercent: 5 }, - { id: "rgnNm", title: "환경청", row:0, rowSpan:2, widthPercent: 7 }, - { id: "B", title: "행정명", row: 0 ,colSpan: 2, widthPercent: 10 }, - { id: "sido", title: "시도", upName:"upChangeRe2", row:1, widthPercent: 5 }, - { id: "gugun", title: "시군구", upName:"upChangeRe2", row:1, widthPercent: 5 }, + { id: "rvbsn", title: "권역", upName: "upChangeRe1", row: 1, widthPercent: 5 }, + { id: "lgbsn", title: "단위유역", upName: "upChangeRe1", row: 1, widthPercent: 5 }, + { id: "mdmbsn", title: "중권역", upName: "upChangeRe1", row: 1, widthPercent: 5 }, + { id: "rgnNm", title: "환경청", row: 0, rowSpan: 2, widthPercent: 7 }, + { id: "B", title: "행정명", row: 0, colSpan: 2, widthPercent: 10 }, + { id: "sido", title: "시도", upName: "upChangeRe2", row: 1, widthPercent: 5 }, + { id: "gugun", title: "시군구", upName: "upChangeRe2", row: 1, widthPercent: 5 }, { id: "bscFctNm", title: "시설명", row: 0, rowSpan: 2, widthPercent: 7 }, { id: "daddr", title: "주소", row: 0, rowSpan: 2 }, - { id: "fctCpc", title: "시설용량\n(m³/일)", align:"right", row: 0, rowSpan: 2, widthPercent: 7 }, - { id: "totInflAmnt", title: "총 유입량\n(m³/일)", align:"right", row: 0, rowSpan: 2, widthPercent: 7 }, - { id: "inflAmnt", title: "하수유입량\n(m³/일)", align:"right", row: 0, rowSpan: 2, widthPercent: 7 }, - { id: "sumFlow", title: '연계처리량\n(m³/일)', align:"right", row: 0, rowSpan: 2, widthPercent: 7 }, - { id: "returnAmnt", title: '총인 반류수량\n(m³/일)', align:"right", row: 0, rowSpan: 2, widthPercent: 7 }, - { id: "efflAmnt", title: '방류량\n(m³/일)', align:"right", row: 0, rowSpan: 2, widthPercent: 7 } + { id: "fctCpc", title: "시설용량\n(m³/일)", align: "right", row: 0, rowSpan: 2, widthPercent: 7 }, + { id: "totInflAmnt", title: "총 유입량\n(m³/일)", align: "right", row: 0, rowSpan: 2, widthPercent: 7 }, + { id: "inflAmnt", title: "하수유입량\n(m³/일)", align: "right", row: 0, rowSpan: 2, widthPercent: 7 }, + { id: "sumFlow", title: '연계처리량\n(m³/일)', align: "right", row: 0, rowSpan: 2, widthPercent: 7 }, + { id: "returnAmnt", title: '총인 반류수량\n(m³/일)', align: "right", row: 0, rowSpan: 2, widthPercent: 7 }, + { id: "efflAmnt", title: '방류량\n(m³/일)', align: "right", row: 0, rowSpan: 2, widthPercent: 7 } ]; -const flowRateList:flowRateListProps[] = [ +const flowRateList: flowRateListProps[] = [ ] /** * (2) 모달 */ -const upHeadListFlowRateModal:TableUpperProps[] = [ - { id: "msrDt", title: "날짜"}, - { id: "totInflAmnt", title: "총 유입량(m³/일)", align:"right"}, - { id: "inflAmnt", title: "하수유입량(m³/일)", align:"right"}, - { id: "sumFlow", title: "연계처리량(m³/일)", align:"right"}, - { id: "returnAmnt", title: "총인 반류수량(m³/일)", align:"right"}, - { id: "efflAmnt", title: "방류량(m³/일)", align:"right"}, - { id: "noProcessInflAmnt", title: "미처리하수 추정량(m³/일)", align:"right"}, - { id: "infoRnDay", title: "강우량(mm/일)", align:"right"} +const upHeadListFlowRateModal: TableUpperProps[] = [ + { id: "msrDt", title: "날짜" }, + { id: "totInflAmnt", title: "총 유입량(m³/일)", align: "right" }, + { id: "inflAmnt", title: "하수유입량(m³/일)", align: "right" }, + { id: "sumFlow", title: "연계처리량(m³/일)", align: "right" }, + { id: "returnAmnt", title: "총인 반류수량(m³/일)", align: "right" }, + { id: "efflAmnt", title: "방류량(m³/일)", align: "right" }, + { id: "noProcessInflAmnt", title: "미처리하수 추정량(m³/일)", align: "right" }, + { id: "infoRnDay", title: "강우량(mm/일)", align: "right" } ] -const modalFlowRateListYear:FlowRateModalProps[] = [ +const modalFlowRateListYear: FlowRateModalProps[] = [ ] /**==================================== @@ -172,13 +172,14 @@ const modalFlowRateListYear:FlowRateModalProps[] = [ ====================================*/ const fetchFlowRateList = async (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => { try { - const tailStr = partCd ? '/'+ partCd + '/' + nameCd + '/' + fcltCpct + '/' + operationValue + (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') : ''; + const tailStr = partCd ? '/' + partCd + '/' + nameCd + '/' + fcltCpct + '/' + operationValue + (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') : ''; const url = '/fclt/flow/' + pageNo + '/' + pageSize + '/' + searchYear + tailStr; const res = await getFetch('fo', url); - return { data: res.data, count: res.count } + return { data: res.data, count: res.count, status: res.status} } catch (error) { console.error('시설정보 유량현황 데이터를 가져오는데 실패했습니다:', error); - return { data: [], count: 0 }; + console.log(error); + return { data: [], count: 0, status: 300 }; } } @@ -186,12 +187,12 @@ export const fetchFlowRateModalList = async (bscFctCd: string, searchYear: strin try { const url = '/fclt/flow/' + bscFctCd + '/' + searchYear; const res = await getFetch('fo', url); - + // msrDt 오름차순으로 정렬 const sortedData = res.data.sort((a: FlowRateModalInterface, b: FlowRateModalInterface) => { return new Date(a.msrDt).getTime() - new Date(b.msrDt).getTime(); }); - + return { data: sortedData } } catch (error) { console.error('시설정보 유량현황 모달 데이터를 가져오는데 실패했습니다:', error); @@ -206,69 +207,69 @@ export const fetchFlowRateModalList = async (bscFctCd: string, searchYear: strin export type FlowRateSearchActions = { searchFlowRateList: (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => void; - - searchFlowRateModalList : (bscFctCd: string, searchYear: string) => void; + + searchFlowRateModalList: (bscFctCd: string, searchYear: string) => void; } export type FlowRateSearchType = { FlowRateSearch: { isInit: boolean; - flowRateSearchLabelArray:string[]; + flowRateSearchLabelArray: string[]; }, - FlowRateList : { - isInit : boolean; + FlowRateList: { + isInit: boolean; upHeadList1: TableUpperProps[]; upHeadList2: TableUpperProps[]; upHeadList3: TableUpperProps[]; - flowRateList:flowRateListProps[]; + flowRateList: flowRateListProps[]; flowRateCount: number; excelList: ExcelType[]; isLoading: boolean; }, - FlowRateModal : { - isInit : boolean; - modalFlowRateListYear : FlowRateModalProps[]; - upHeadListFlowRateModalYear : TableUpperProps[]; + FlowRateModal: { + isInit: boolean; + modalFlowRateListYear: FlowRateModalProps[]; + upHeadListFlowRateModalYear: TableUpperProps[]; } } export type FlowRateSearchStore = FlowRateSearchType & FlowRateSearchActions; -const dataFormSetting=(item : flowRateListProps | FlowRateModalProps)=> { +export const dataFormSetting = (item: flowRateListProps | FlowRateModalProps | FlowRateModalInterface) => { DataForm({ type: 'date', column: 'msrDt', item: item as unknown as Record<string, unknown> }); - DataForm({ type: 'float0', column: 'totInflAmnt', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float0', column: 'inflAmnt', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float0', column: 'sumFlow', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float0', column: 'efflWqltBod', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float0', column: 'returnAmnt', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float0', column: 'efflAmnt', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float0', column: 'totInflAmnt', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float0', column: 'inflAmnt', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float0', column: 'sumFlow', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float0', column: 'efflWqltBod', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float0', column: 'returnAmnt', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float0', column: 'efflAmnt', item: item as unknown as Record<string, unknown> }); } -export const flowRateSearchInitState:FlowRateSearchType = { +export const flowRateSearchInitState: FlowRateSearchType = { FlowRateSearch: { isInit: false, flowRateSearchLabelArray: FlowRateSearchLabelArray, }, - FlowRateList : { - isInit : false, - upHeadList1 : upHeadList1, - upHeadList2 : upHeadList2, - upHeadList3 : upHeadList3, - flowRateList : flowRateList, - flowRateCount : 0, + FlowRateList: { + isInit: false, + upHeadList1: upHeadList1, + upHeadList2: upHeadList2, + upHeadList3: upHeadList3, + flowRateList: flowRateList, + flowRateCount: 0, excelList: [], isLoading: true, }, - FlowRateModal : { - isInit : false, - upHeadListFlowRateModalYear : upHeadListFlowRateModal, - modalFlowRateListYear : modalFlowRateListYear, + FlowRateModal: { + isInit: false, + upHeadListFlowRateModalYear: upHeadListFlowRateModal, + modalFlowRateListYear: modalFlowRateListYear, } } -export const flowRateSearchReducer:(set:(fn: (state: FlowRateSearchType) => FlowRateSearchType) => void)=>FlowRateSearchActions=(set: (fn: (state: FlowRateSearchType) => FlowRateSearchType) => void) => { +export const flowRateSearchReducer: (set: (fn: (state: FlowRateSearchType) => FlowRateSearchType) => void) => FlowRateSearchActions = (set: (fn: (state: FlowRateSearchType) => FlowRateSearchType) => void) => { return { searchFlowRateList: async (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => { set((state: FlowRateSearchType) => ({ @@ -278,17 +279,50 @@ export const flowRateSearchReducer:(set:(fn: (state: FlowRateSearchType) => Flow isLoading: true, }, })); - const res = await fetchFlowRateList(pageNo, pageSize, searchYear, partCd, nameCd, fcltCpct, operationValue, fcltyNm); - const data = res.data; + + // const res = await fetchFlowRateList(pageNo, pageSize, searchYear, partCd, nameCd, fcltCpct, operationValue, fcltyNm); + + //--------- + const flowRateList: any[] = []; + const results: any[] = []; + if (pageSize < 5000) { + const res = await fetchFlowRateList(pageNo, pageSize, searchYear, partCd, nameCd, fcltCpct, operationValue, fcltyNm); + flowRateList.push(...res.data); + results.push(res); + } else { + for (let idx = 0; idx < pageSize; idx += 100) { + const tmpPageNo = Math.floor(idx / 100) + 1; + const tmpPageSize = Math.min(100, pageSize - idx); + const result = await fetchFlowRateList( tmpPageNo, tmpPageSize, searchYear, partCd, nameCd, fcltCpct, operationValue, fcltyNm); + results.push(result); + if (result?.count < tmpPageNo * tmpPageSize) { + break; // 더 이상 데이터가 없으면 중지 + } + } + // console.log(results); + // 결과를 합치기 + + for (const res of results) { + if (res?.status === 200 && Array.isArray(res?.data)) { + flowRateList.push(...res.data); + } + } + } + //--------- + const recordCount = results[0]?.count || 0; + //------- + + // const data = res.data; + const data = flowRateList; const excelDownList: ExcelType[] = []; const rtnData = data.map((item: flowRateListProps) => { item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, '')); - item.totInflAmnt = Number(item.inflAmnt || 0) + Number(item.sumFlow || 0) + Number(item.returnAmnt || 0) + item.totInflAmnt = Number(item.inflAmnt || 0) + Number(item.sumFlow || 0) + Number(item.returnAmnt || 0); // 하수유입량 + 연계처리량 + 반류수유량(m³/일) dataFormSetting(item); return item; }); - if(pageSize>5000){ + if (pageSize > 5000) { rtnData.forEach((item: flowRateListProps) => { excelDownList.push({ 시설코드: item.bscFctCd, @@ -312,15 +346,15 @@ export const flowRateSearchReducer:(set:(fn: (state: FlowRateSearchType) => Flow set((state: FlowRateSearchType) => ({ ...state, - FlowRateList: pageSize<5000?{ + FlowRateList: pageSize < 5000 ? { ...state.FlowRateList, flowRateList: rtnData, - flowRateCount: res.count, - isLoading : false, - }:{ + flowRateCount: recordCount, + isLoading: false, + } : { ...state.FlowRateList, excelList: excelDownList, - isLoading : false, + isLoading: false, }, })) }, @@ -329,44 +363,48 @@ export const flowRateSearchReducer:(set:(fn: (state: FlowRateSearchType) => Flow const inflData = CalcRainEffect(res.data); const res2 = await fetchFacilityWaterQltyListHist(searchYear, bscFctCd); - const wqltData = CalcRainEffect(res2?.data.wqltObjs); - + const wqltData = res2?.data.wqltObjs; + //const wqltData = CalcRainEffect(res2?.data.wqltObjs); /** - * [(강우시 일별유입유량*강우시 일별유입BOD)-(청천시 연평균유입유량*청천시 연평균유입BOD)]​ - /강우시 일별유입BOD) + * [(강우시 일별유입유량*강우시 일별유입BOD)-(청천시 연평균유입유량*청천시 연평균유입BOD)] / 강우시 일별유입BOD */ - //let rainInflAmnt = 0; - let sunnyInflAmnt = 0; - let sunnyBod = 0; - //let rainDay = 0; + let sunnyInflAmnt = 0; // 청천시 일별유입유량 + let sunnyBod = 0; // 청천시 일별유입BOD let sunnyDay = 0; - - inflData.forEach(item => { - if(item.rainEffectDay == 1){ - //rainDay += 1; - //rainInflAmnt += Number(item.inflAmnt); + let rainEffectDay = 0; + wqltData?.forEach(item => { + if (item.infoRnDay == null) item.infoRnDay = 0; + if (Number(item.infoRnDay) >= 3) { + rainEffectDay = 2; } else { - const msrDt = item.msrDt.replaceAll('-',''); + rainEffectDay--; + } + if (rainEffectDay < 0) { //청천시 sunnyDay += 1; sunnyInflAmnt += Number(item.inflAmnt); - sunnyBod += Number(wqltData.filter((wqitem)=>wqitem.msrDt === msrDt)[0].inflAssctBfrWqltBod); + sunnyBod += Number(item.inflAssctBfrWqltBod); } }); - //const yearRainAmnt = rainInflAmnt===0?0:rainInflAmnt / rainDay; - const yearSunnyAmnt = sunnyInflAmnt===0?0:sunnyInflAmnt / sunnyDay; - const yearSunnyBod = sunnyBod===0?0:sunnyBod / sunnyDay; - + const yearSunnyAmnt = sunnyDay > 0 ? sunnyInflAmnt / sunnyDay : 0; // 청천시 연평균유입유량 + const yearSunnyBod = sunnyDay > 0 ? sunnyBod / sunnyDay : 0; // 청천시 연평균유입BOD + //console.log(inflData); const rtnData = inflData.map((item: FlowRateModalProps) => { - const dayBod = Number(wqltData.filter(wqitem=>wqitem.msrDt === item.msrDt.replaceAll('-',''))[0].inflAssctBfrWqltBod); - const noProcessInflAmnt = roundNumber(((Number(item.inflAmnt) * dayBod) - (yearSunnyAmnt * yearSunnyBod)) / dayBod, 0) - - item.totInflAmnt = Number(item.inflAmnt || 0) + Number(item.sumFlow || 0) + Number(item.returnAmnt || 0); - item.noProcessInflAmnt = noProcessInflAmnt && Number(noProcessInflAmnt.replaceAll(',','')) > 0 ? noProcessInflAmnt : ''; - dataFormSetting(item); - return item; + const targetWqItem = wqltData?.find(wqitem => wqitem.msrDt === item.msrDt.replaceAll('-', '')); + const dayBod = targetWqItem ? Number(targetWqItem.inflAssctBfrWqltBod) : 0; + const noProcessInflAmnt = Math.round(((Number(item.inflAmnt) * dayBod) - (yearSunnyAmnt * yearSunnyBod)) / dayBod) + const totInflAmnt = Number(item.inflAmnt || 0) + Number(item.sumFlow || 0) + Number(item.returnAmnt || 0); + + return { + ...item, + totInflAmnt, + noProcessInflAmnt: noProcessInflAmnt && noProcessInflAmnt > 0 ? noProcessInflAmnt : '', + msrDt: (item.msrDt.length === 8 && !item.msrDt.includes('-')) ? + `${item.msrDt.slice(0, 4)}-${item.msrDt.slice(4, 6)}-${item.msrDt.slice(6, 8)}` + : item.msrDt, + }; }); - + //console.log(rtnData); set((state: FlowRateSearchType) => ({ ...state, FlowRateModal: { diff --git a/apps/fo/app/store/planSewage/index.tsx b/apps/fo/app/store/planSewage/index.tsx index 2fd9b531c..1f81dec01 100644 --- a/apps/fo/app/store/planSewage/index.tsx +++ b/apps/fo/app/store/planSewage/index.tsx @@ -4,6 +4,7 @@ import { getFetch, postFetch } from "@/app/components/api"; import { type ApiResponse } from "../underWaterInput"; // import CalcRainEffect from "@/app/components/common/CalcRainEffect"; import { roundUp } from "../common"; +// import { fetchSweageOccurListFlow, fetchWtrUsgYmList } from "../sewageOccur"; export type { TableUpperProps }; /** * 처리시설 연간 운영현황 > 계획하수량 산정지표 현황 @@ -101,14 +102,15 @@ const planSewageList: PlanSewageProps[] = [ * @param oprtngSttsCd : 운영 상태 코드 * @param facilityName : 시설명 */ -const fetchPlanSewageList = async (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => { +const fetchPlanSewageList = async (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string + , fcltCpct?: string, operationValue?: string, fcltyNm?: string) => { const tailStr = partCd ? '/' + partCd + '/' + (nameCd || '0') + '/' + (fcltCpct || '0') + '/' + (operationValue || '0') + '/0' + (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') : ''; const url = '/prcsFclt/info/plan/' + pageNo + '/' + pageSize + '/' + searchYear + tailStr; const res = await getFetch('fo', url); return { data: res.data, count: res.count } } -// 분류식화율 구하기 위한 api 호출 +// 분류식화율 구하기 위한 api 호출 : 분류식화율 = 분류식지역 면적(㎢) / 처리​구역 면적합계(㎢) × 100 const fetchPlanZoneList = async (searchYear: string, fcltCdList: string[]) => { try { const res = await postFetch('fo', '/prcsFclt/info/planZone/' + searchYear, fcltCdList); @@ -251,19 +253,50 @@ export const planSewageReducer: (set: (fn: (state: PlanSewageType) => PlanSewage const excelDownList: ExcelType[] = []; // const data = res.data - const planZoneRes: any = await fetchPlanZoneList(searchYear, fcltCdList); - const planZoneList = planZoneRes?.data; + // 분류식화율 구하기 위한 api 호출 : 분류식화율 = 분류식지역 면적(㎢) / 처리​구역 면적합계(㎢) × 100 + // const planZoneRes: any = await fetchPlanZoneList(searchYear, fcltCdList); + // const detailRes: any = await fetchFlow(searchYear, fcltCdList); + //--------- + const planZoneList: any[] = []; + const flowDetailList: Record<string, any[]> = {}; + // const results: any[] = []; + const flowDetailResults: any[] = []; + for (let idx = 0; idx < fcltCdList.length; idx += 100) { + const slicedItems = fcltCdList.slice(idx, idx + 100); + const result = await fetchPlanZoneList(searchYear, slicedItems); // 분류식화율 구하기 위한 api 호출 : 분류식화율 = 분류식지역 면적(㎢) / 처리​구역 면적합계(㎢) × 100 + const flowResult = await fetchFlow(searchYear, slicedItems); + if (result?.status === 200 && Array.isArray(result?.data)) { + planZoneList.push(...result.data); + } + flowDetailResults.push(flowResult); + } + //console.log(results); + // 결과를 합치기 + for (const res of flowDetailResults) { + if (res.status === 200 && res.data) { + for (const [key, value] of Object.entries(res.data)) { + const keyStr = String(key); + if (!flowDetailList[keyStr]) flowDetailList[keyStr] = []; + flowDetailList[keyStr].push(...(value as any[])); + } + } + } + //------- + + + // const planZoneList = planZoneRes?.data; planZoneList?.forEach(item => { const planSewageItem = planSewageList.find(item2 => item2.bscFctCd == item.bscFctCd); if (planSewageItem) { - planSewageItem.sprtProcZoneAr = item.sprtProcZoneAr; - planSewageItem.procZoneSm = item.procZoneSm; + planSewageItem.sprtProcZoneAr = item.sprtProcZoneAr; // 처리구역면적_분류식 + planSewageItem.procZoneSm = item.procZoneSm; //처리구역면적합계 } }); - const detailRes: any = await fetchFlow(searchYear, fcltCdList); + //const detailRes: any = await fetchFlow(searchYear, fcltCdList); for (const item of planSewageList) { - const detailList = detailRes?.data[item.bscFctCd]; + // const detailList = detailRes?.data[item.bscFctCd]; + const detailList = flowDetailList[item.bscFctCd]; let avgInflAmnt = 0; let sumInflAmntSunny = 0; diff --git a/apps/fo/app/store/processArea/index.tsx b/apps/fo/app/store/processArea/index.tsx index 959b83794..31da21bfc 100644 --- a/apps/fo/app/store/processArea/index.tsx +++ b/apps/fo/app/store/processArea/index.tsx @@ -12,52 +12,52 @@ export type { TableUpperProps }; */ // 처리구역 리스트 export type ProcessAreaProps = { - swtrmtFcltCd : string, + swtrmtFcltCd: string, ctpvNm?: string, sggNm?: string, - swtrmtFcltNm : string, - location : string, - fctCpc : number | '', - fcltCpctSum : number | '', - ruse : boolean, - planYrDt4 : string, - planYr : string, - planYrDc1 : string, - planYrDc2 : string, - planYrDc3 : string, - planYrDc4 : string, + swtrmtFcltNm: string, + location: string, + fctCpc: number | '', + fcltCpctSum: number | '', + ruse: boolean, + planYrDt4: string, + planYr: string, + planYrDc1: string, + planYrDc2: string, + planYrDc3: string, + planYrDc4: string, /** * 증설계획 */ // 기승인(당초4단계), 현재, 변경1~4 - planSwageQntSumDt4 : number | '', - planSwageQntSum : number | '', - planSwageQntSumDc1 : number | '', - planSwageQntSumDc2 : number | '', - planSwageQntSumDc3 : number | '', - planSwageQntSumDc4 : number | '', + planSwageQntSumDt4: number | '', + planSwageQntSum: number | '', + planSwageQntSumDc1: number | '', + planSwageQntSumDc2: number | '', + planSwageQntSumDc3: number | '', + planSwageQntSumDc4: number | '', /** * 처리인구 */ // 기승인(당초4단계), 변경1~4 - swtrmtPpltnCntDt4 : number | '', - swtrmtPpltnCnt : number | '', - swtrmtPpltnCntDc1 : number | '', - swtrmtPpltnCntDc2 : number | '', - swtrmtPpltnCntDc3 : number | '', - swtrmtPpltnCntDc4 : number | '', + swtrmtPpltnCntDt4: number | '', + swtrmtPpltnCnt: number | '', + swtrmtPpltnCntDc1: number | '', + swtrmtPpltnCntDc2: number | '', + swtrmtPpltnCntDc3: number | '', + swtrmtPpltnCntDc4: number | '', /** * 처리구역면적 */ // 기승인(당초4단계), 변경1~4 - swtrmtZoneAreaDt4 : number | '', - swtrmtZoneArea : number | '', - swtrmtZoneAreaDc1 : number | '', - swtrmtZoneAreaDc2 : number | '', - swtrmtZoneAreaDc3 : number | '', - swtrmtZoneAreaDc4 : number | '', + swtrmtZoneAreaDt4: number | '', + swtrmtZoneArea: number | '', + swtrmtZoneAreaDc1: number | '', + swtrmtZoneAreaDc2: number | '', + swtrmtZoneAreaDc3: number | '', + swtrmtZoneAreaDc4: number | '', // 처리구역 - trtDstr : string | '', + trtDstr: string | '', } export interface ExcelType { @@ -89,32 +89,32 @@ export interface ExcelType { // (2) 처리구역 팝업 // 시설증설 계획 export type FcltyImproveInfoProps = { - planSwageQntSumDt4 : number | '', - planSwageQntSum : number | '', - planSwageQntSumDc1 : number | '', - planSwageQntSumDc2 : number | '', - planSwageQntSumDc3 : number | '', - planSwageQntSumDc4 : number | '', + planSwageQntSumDt4: number | '', + planSwageQntSum: number | '', + planSwageQntSumDc1: number | '', + planSwageQntSumDc2: number | '', + planSwageQntSumDc3: number | '', + planSwageQntSumDc4: number | '', } // 처리인구 계획 export type ProcessPopulationProps = { - swtrmtPpltnCntDt4 : number | '', - swtrmtPpltnCnt : number | '', - swtrmtPpltnCntDc1 : number | '', - swtrmtPpltnCntDc2 : number | '', - swtrmtPpltnCntDc3 : number | '', - swtrmtPpltnCntDc4 : number | '', + swtrmtPpltnCntDt4: number | '', + swtrmtPpltnCnt: number | '', + swtrmtPpltnCntDc1: number | '', + swtrmtPpltnCntDc2: number | '', + swtrmtPpltnCntDc3: number | '', + swtrmtPpltnCntDc4: number | '', } // 처리구역 계획 export type ProcessAreaPlanProps = { - swtrmtZoneAreaDt4 : number | '', - swtrmtZoneArea : number | '', - swtrmtZoneAreaDc1 : number | '', - swtrmtZoneAreaDc2 : number | '', - swtrmtZoneAreaDc3 : number | '', - swtrmtZoneAreaDc4 : number | '', - trtDstr : string, + swtrmtZoneAreaDt4: number | '', + swtrmtZoneArea: number | '', + swtrmtZoneAreaDc1: number | '', + swtrmtZoneAreaDc2: number | '', + swtrmtZoneAreaDc3: number | '', + swtrmtZoneAreaDc4: number | '', + trtDstr: string, } /**==================================== @@ -123,93 +123,93 @@ export type ProcessAreaPlanProps = { /** * (1) 메인페이지 - */ + */ // 검색단 -const ProcessFacilityLabelArray:string[] = ['구분', '시도', '시군구','용량별', '시설명', '시설상태', '연도별', '권역', '단위유역', '중권역', '환경청별']; +const ProcessFacilityLabelArray: string[] = ['구분', '시도', '시군구', '용량별', '시설명', '시설상태', '연도별', '권역', '단위유역', '중권역', '환경청별']; // 리스트 헤더(행정별) -const upHeadList1:TableUpperProps[] = [ - { id: "A", title: "행정구역", colSpan:2, rowSpan:2, row:0 }, - { id: "ctpvNm", title: "시도", upName:"upChangeRe", row:2, widthPercent:4 }, - { id: "sggNm", title: "시군구", upName:"upChangeRe", row:2, widthPercent:4 }, - { id: "swtrmtFcltNm", title: "시설명", rowSpan:3, row:0, widthPercent: 6 }, - { id: "location", title: "주소", rowSpan:3, row:0, widthPercent: 12 }, - { id: "fctCpc", title: "시설용량(m³/일)", align:"right", rowSpan:3, row:0, widthPercent: 5 }, - { id: "B", title: "시설계획", colSpan:19, row: 0 }, - { id: "C", title: "증설계획(m³/일)", colSpan:6, row: 1 }, - { id: "planSwageQntSumDt4", title: "기승인\n4단계", row:2, align:"right", widthPercent: 3.5 }, - { id: "planSwageQntSum", title: "현재", row:2, align:"right", widthPercent: 3.5 }, - { id: "planSwageQntSumDc1", title: "1단계", row:2, align:"right", widthPercent: 3.5 }, - { id: "planSwageQntSumDc2", title: "2단계", row:2, align:"right", widthPercent: 3.5 }, - { id: "planSwageQntSumDc3", title: "3단계", row:2, align:"right", widthPercent: 3.5 }, - { id: "planSwageQntSumDc4", title: "4단계", row:2, align:"right", widthPercent: 3.5 }, - { id: "D", title: "처리인구(인)", colSpan:6, row:1 }, - { id: "swtrmtPpltnCntDt4", title: "기승인\n4단계", row:2, align:"right", widthPercent: 3.5 }, - { id: "swtrmtPpltnCnt", title: "현재", row:2, align:"right", widthPercent: 3.5 }, - { id: "swtrmtPpltnCntDc1", title: "1단계", row:2, align:"right", widthPercent: 3.5 }, - { id: "swtrmtPpltnCntDc2", title: "2단계", row:2, align:"right", widthPercent: 3.5 }, - { id: "swtrmtPpltnCntDc3", title: "3단계", row:2, align:"right", widthPercent: 3.5 }, - { id: "swtrmtPpltnCntDc4", title: "4단계", row:2, align:"right", widthPercent: 3.5 }, - { id: "E", title: "처리구역(km²)", colSpan:7, row:1 }, - { id: "swtrmtZoneAreaDt4", title: "기승인\n4단계", row:2, align:"right", widthPercent: 3.5 }, - { id: "swtrmtZoneArea", title: "현재", row:2, align:"right", widthPercent: 3.5 }, - { id: "swtrmtZoneAreaDc1", title: "1단계", row:2, align:"right", widthPercent: 3.5 }, - { id: "swtrmtZoneAreaDc2", title: "2단계", row:2, align:"right", widthPercent: 3.5 }, - { id: "swtrmtZoneAreaDc3", title: "3단계", row:2, align:"right", widthPercent: 3.5 }, - { id: "swtrmtZoneAreaDc4", title: "4단계", row:2, align:"right", widthPercent: 3.5 }, - { id: "trtDstr", title: "처리구역", row:2} +const upHeadList1: TableUpperProps[] = [ + { id: "A", title: "행정구역", colSpan: 2, rowSpan: 2, row: 0 }, + { id: "ctpvNm", title: "시도", upName: "upChangeRe", row: 2, widthPercent: 4 }, + { id: "sggNm", title: "시군구", upName: "upChangeRe", row: 2, widthPercent: 4 }, + { id: "swtrmtFcltNm", title: "시설명", rowSpan: 3, row: 0, widthPercent: 6 }, + { id: "location", title: "주소", rowSpan: 3, row: 0, widthPercent: 12 }, + { id: "fctCpc", title: "시설용량(m³/일)", align: "right", rowSpan: 3, row: 0, widthPercent: 5 }, + { id: "B", title: "시설계획", colSpan: 18, row: 0 }, + { id: "C", title: "증설계획(m³/일)", colSpan: 6, row: 1 }, + { id: "planSwageQntSumDt4", title: "기승인\n4단계", row: 2, align: "right", widthPercent: 3.5 }, + { id: "planSwageQntSum", title: "현재", row: 2, align: "right", widthPercent: 3.5 }, + { id: "planSwageQntSumDc1", title: "1단계", row: 2, align: "right", widthPercent: 3.5 }, + { id: "planSwageQntSumDc2", title: "2단계", row: 2, align: "right", widthPercent: 3.5 }, + { id: "planSwageQntSumDc3", title: "3단계", row: 2, align: "right", widthPercent: 3.5 }, + { id: "planSwageQntSumDc4", title: "4단계", row: 2, align: "right", widthPercent: 3.5 }, + { id: "D", title: "처리인구(인)", colSpan: 6, row: 1 }, + { id: "swtrmtPpltnCntDt4", title: "기승인\n4단계", row: 2, align: "right", widthPercent: 3.5 }, + { id: "swtrmtPpltnCnt", title: "현재", row: 2, align: "right", widthPercent: 3.5 }, + { id: "swtrmtPpltnCntDc1", title: "1단계", row: 2, align: "right", widthPercent: 3.5 }, + { id: "swtrmtPpltnCntDc2", title: "2단계", row: 2, align: "right", widthPercent: 3.5 }, + { id: "swtrmtPpltnCntDc3", title: "3단계", row: 2, align: "right", widthPercent: 3.5 }, + { id: "swtrmtPpltnCntDc4", title: "4단계", row: 2, align: "right", widthPercent: 3.5 }, + { id: "E", title: "처리구역(km²)", colSpan: 6, row: 1 }, + { id: "swtrmtZoneAreaDt4", title: "기승인\n4단계", row: 2, align: "right", widthPercent: 3.5 }, + { id: "swtrmtZoneArea", title: "현재", row: 2, align: "right", widthPercent: 3.5 }, + { id: "swtrmtZoneAreaDc1", title: "1단계", row: 2, align: "right", widthPercent: 3.5 }, + { id: "swtrmtZoneAreaDc2", title: "2단계", row: 2, align: "right", widthPercent: 3.5 }, + { id: "swtrmtZoneAreaDc3", title: "3단계", row: 2, align: "right", widthPercent: 3.5 }, + { id: "swtrmtZoneAreaDc4", title: "4단계", row: 2, align: "right", widthPercent: 3.5 }, + { id: "trtDstr", title: "처리구역", row: 0, rowSpan: 3 } // row - header에서 몇번째 행인지 ]; -const processAreaList:ProcessAreaProps[] = [ +const processAreaList: ProcessAreaProps[] = [ ] /** * 처리구역정보 상세팝업 - */ + */ // 시설증설 계획 (헤더) -const upHeadListFcltyImproveInfo:TableUpperProps[] = [ - { id: "planSwageQntSumDt4", title: "기승인\n4단계(m³/일)", rowSpan:2, row:0, align:"right"}, - { id: "A", title: "증설계획(m³/일)", colSpan:5, row:0}, - { id: "planSwageQntSum", title: "현재", row:1, align:"right"}, - { id: "planSwageQntSumDc1", title: "1단계", row:1, align:"right"}, - { id: "planSwageQntSumDc2", title: "2단계", row:1, align:"right"}, - { id: "planSwageQntSumDc3", title: "3단계", row:1, align:"right"}, - { id: "planSwageQntSumDc4", title: "4단계", row:1, align:"right"}, +const upHeadListFcltyImproveInfo: TableUpperProps[] = [ + { id: "planSwageQntSumDt4", title: "기승인\n4단계(m³/일)", rowSpan: 2, row: 0, align: "right" }, + { id: "A", title: "증설계획(m³/일)", colSpan: 5, row: 0 }, + { id: "planSwageQntSum", title: "현재", row: 1, align: "right" }, + { id: "planSwageQntSumDc1", title: "1단계", row: 1, align: "right" }, + { id: "planSwageQntSumDc2", title: "2단계", row: 1, align: "right" }, + { id: "planSwageQntSumDc3", title: "3단계", row: 1, align: "right" }, + { id: "planSwageQntSumDc4", title: "4단계", row: 1, align: "right" }, ]; // 시설증설 계획 (리스트) -const gridListFcltyImproveInfo:FcltyImproveInfoProps[] = [ +const gridListFcltyImproveInfo: FcltyImproveInfoProps[] = [ ]; // 처리인구 계획 (헤더) -const upHeadListProcessPopulation:TableUpperProps[] = [ - { id: "swtrmtPpltnCntDt4", title: "기승인\n4단계(인)", rowSpan:2, row:0, align:"right"}, - { id: "A", title: "처리인구(인)", colSpan:5, row:0}, - { id: "swtrmtPpltnCnt", title: "현재", row:1, align:"right"}, - { id: "swtrmtPpltnCntDc1", title: "1단계", row:1, align:"right"}, - { id: "swtrmtPpltnCntDc2", title: "2단계", row:1, align:"right"}, - { id: "swtrmtPpltnCntDc3", title: "3단계", row:1, align:"right"}, - { id: "swtrmtPpltnCntDc4", title: "4단계", row:1, align:"right"}, +const upHeadListProcessPopulation: TableUpperProps[] = [ + { id: "swtrmtPpltnCntDt4", title: "기승인\n4단계(인)", rowSpan: 2, row: 0, align: "right" }, + { id: "A", title: "처리인구(인)", colSpan: 5, row: 0 }, + { id: "swtrmtPpltnCnt", title: "현재", row: 1, align: "right" }, + { id: "swtrmtPpltnCntDc1", title: "1단계", row: 1, align: "right" }, + { id: "swtrmtPpltnCntDc2", title: "2단계", row: 1, align: "right" }, + { id: "swtrmtPpltnCntDc3", title: "3단계", row: 1, align: "right" }, + { id: "swtrmtPpltnCntDc4", title: "4단계", row: 1, align: "right" }, ]; // 처리인구 계획 (리스트) -const gridListProcessPopulation:ProcessPopulationProps[] = [ +const gridListProcessPopulation: ProcessPopulationProps[] = [ ]; // 처리구역 계획 (헤더) -const upHeadListProcessAreaPlan:TableUpperProps[] = [ - { id: "swtrmtZoneAreaDt4", title: "기승인\n4단계(km²)", rowSpan:2, row:0, align:"right"}, - { id: "A", title: "처리면적(km²)", colSpan:6, row:0, align:"right"}, - { id: "swtrmtZoneArea", title: "현재", row:1, align:"right"}, - { id: "swtrmtZoneAreaDc1", title: "1단계", row:1, align:"right"}, - { id: "swtrmtZoneAreaDc2", title: "2단계", row:1, align:"right"}, - { id: "swtrmtZoneAreaDc3", title: "3단계", row:1, align:"right"}, - { id: "swtrmtZoneAreaDc4", title: "4단계", row:1, align:"right"}, - { id: "trtDstr", title: "처리구역", row:1} +const upHeadListProcessAreaPlan: TableUpperProps[] = [ + { id: "swtrmtZoneAreaDt4", title: "기승인\n4단계(km²)", rowSpan: 2, row: 0, align: "right" }, + { id: "A", title: "처리면적(km²)", colSpan: 5, row: 0, align: "right" }, + { id: "swtrmtZoneArea", title: "현재", row: 1, align: "right" }, + { id: "swtrmtZoneAreaDc1", title: "1단계", row: 1, align: "right" }, + { id: "swtrmtZoneAreaDc2", title: "2단계", row: 1, align: "right" }, + { id: "swtrmtZoneAreaDc3", title: "3단계", row: 1, align: "right" }, + { id: "swtrmtZoneAreaDc4", title: "4단계", row: 1, align: "right" }, + // { id: "trtDstr", title: "처리구역", row: 1 } ]; // 시설증설 계획 (리스트) -const gridListProcessAreaPlan:ProcessAreaPlanProps[] = [ +const gridListProcessAreaPlan: ProcessAreaPlanProps[] = [ ]; /**==================================== @@ -227,113 +227,115 @@ const gridListProcessAreaPlan:ProcessAreaPlanProps[] = [ } }; */ -const fetchFacilityDetailInfo = async (pageNo:number, pageSize:number, swtrmtFcltCd: string[]) : Promise<{data:any[], count:number}> => { - try { - const res = await postFetch('fo', '/prcsFclt/ruseInfo/'+pageNo+'/'+pageSize, swtrmtFcltCd); - return {data: res.data, count:res.count}; - } catch (error) { - console.error('시설정보 데이터를 가져오는데 실패했습니다:', error); - return {data:[], count:0}; - } +const fetchFacilityDetailInfo = async (pageNo: number, pageSize: number, swtrmtFcltCd: string[]): Promise<{ data: any[], count: number }> => { + try { + const res = await postFetch('fo', '/prcsFclt/ruseInfo/' + pageNo + '/' + pageSize, swtrmtFcltCd); + return { data: res.data, count: res.count }; + } catch (error) { + console.error('시설정보 데이터를 가져오는데 실패했습니다:', error); + return { data: [], count: 0 }; + } }; // 시설정보 리스트 데이터 호출(구분 선택 후 시설명 있을 경우) -const fetchProcessAreaList = async (pageNo : number, pageSize : number, ctpvNm : string | undefined, sggNm : string | undefined, targetYear : string | undefined, set : (fn: (state: ProcessAreaType) => ProcessAreaType) => void): Promise<{data:any[], count:number}> => { - try { - const res = await fetchPlanInfo(targetYear, ctpvNm, sggNm, undefined); - if(res && res.length > 0){ - const enrichedData = await Promise.all( - res.map(async (item: ProcessAreaProps) => { - upHeadList1.forEach((head:TableUpperProps) => { - /* if (head.id === 'planSwageQntSum' || head.id === 'swtrmtPpltnCnt' || head.id === 'swtrmtZoneArea') { - head.title = '현재\n(' + item.planYr + '년)'; - } else if(head.id === 'planSwageQntSumDc1' || head.id === 'swtrmtPpltnCntDc1' || head.id === 'swtrmtZoneAreaDc1') { - head.title = '1단계\n(' + item.planYrDc1 + '년)'; - } else if(head.id === 'planSwageQntSumDc2' || head.id === 'swtrmtPpltnCntDc2' || head.id === 'swtrmtZoneAreaDc2') { - head.title = '2단계\n(' + item.planYrDc2 + '년)'; - } else if(head.id === 'planSwageQntSumDc3' || head.id === 'swtrmtPpltnCntDc3' || head.id === 'swtrmtZoneAreaDc3') { - head.title = '3단계\n(' + item.planYrDc3 + '년)'; - } else if(head.id === 'planSwageQntSumDc4' || head.id === 'swtrmtPpltnCntDc4' || head.id === 'swtrmtZoneAreaDc4') { - head.title = '4단계\n(' + item.planYrDc4 + '년)'; - } else if(head.id === 'planSwageQntSumDt4' || head.id === 'swtrmtPpltnCntDt4' || head.id === 'swtrmtZoneAreaDt4') { - head.title = '기승인\n4단계(' + item.planYrDt4 + '년)'; - } */ - if (head.id === 'planSwageQntSum' || head.id === 'swtrmtPpltnCnt' || head.id === 'swtrmtZoneArea') { - head.title = '현재'; - } else if(head.id === 'planSwageQntSumDc1' || head.id === 'swtrmtPpltnCntDc1' || head.id === 'swtrmtZoneAreaDc1') { - head.title = '1단계'; - } else if(head.id === 'planSwageQntSumDc2' || head.id === 'swtrmtPpltnCntDc2' || head.id === 'swtrmtZoneAreaDc2') { - head.title = '2단계'; - } else if(head.id === 'planSwageQntSumDc3' || head.id === 'swtrmtPpltnCntDc3' || head.id === 'swtrmtZoneAreaDc3') { - head.title = '3단계'; - } else if(head.id === 'planSwageQntSumDc4' || head.id === 'swtrmtPpltnCntDc4' || head.id === 'swtrmtZoneAreaDc4') { - head.title = '4단계'; - } else if(head.id === 'planSwageQntSumDt4' || head.id === 'swtrmtPpltnCntDt4' || head.id === 'swtrmtZoneAreaDt4') { - head.title = '기승인\n4단계'; - } - }); - return item; +const fetchProcessAreaList = async (pageNo: number, pageSize: number, ctpvNm: string | undefined, sggNm: string | undefined + , targetYear: string | undefined, set: (fn: (state: ProcessAreaType) => ProcessAreaType) => void): Promise<{ data: any[], count: number }> => { + try { + const res = await fetchPlanInfo(targetYear, ctpvNm, sggNm, undefined); + if (res && res.length > 0) { + const enrichedData = await Promise.all( + res.map(async (item: ProcessAreaProps) => { + upHeadList1.forEach((head: TableUpperProps) => { + /* if (head.id === 'planSwageQntSum' || head.id === 'swtrmtPpltnCnt' || head.id === 'swtrmtZoneArea') { + head.title = '현재\n(' + item.planYr + '년)'; + } else if(head.id === 'planSwageQntSumDc1' || head.id === 'swtrmtPpltnCntDc1' || head.id === 'swtrmtZoneAreaDc1') { + head.title = '1단계\n(' + item.planYrDc1 + '년)'; + } else if(head.id === 'planSwageQntSumDc2' || head.id === 'swtrmtPpltnCntDc2' || head.id === 'swtrmtZoneAreaDc2') { + head.title = '2단계\n(' + item.planYrDc2 + '년)'; + } else if(head.id === 'planSwageQntSumDc3' || head.id === 'swtrmtPpltnCntDc3' || head.id === 'swtrmtZoneAreaDc3') { + head.title = '3단계\n(' + item.planYrDc3 + '년)'; + } else if(head.id === 'planSwageQntSumDc4' || head.id === 'swtrmtPpltnCntDc4' || head.id === 'swtrmtZoneAreaDc4') { + head.title = '4단계\n(' + item.planYrDc4 + '년)'; + } else if(head.id === 'planSwageQntSumDt4' || head.id === 'swtrmtPpltnCntDt4' || head.id === 'swtrmtZoneAreaDt4') { + head.title = '기승인\n4단계(' + item.planYrDt4 + '년)'; + } */ + if (head.id === 'planSwageQntSum' || head.id === 'swtrmtPpltnCnt' || head.id === 'swtrmtZoneArea') { + head.title = '현재'; + } else if (head.id === 'planSwageQntSumDc1' || head.id === 'swtrmtPpltnCntDc1' || head.id === 'swtrmtZoneAreaDc1') { + head.title = '1단계'; + } else if (head.id === 'planSwageQntSumDc2' || head.id === 'swtrmtPpltnCntDc2' || head.id === 'swtrmtZoneAreaDc2') { + head.title = '2단계'; + } else if (head.id === 'planSwageQntSumDc3' || head.id === 'swtrmtPpltnCntDc3' || head.id === 'swtrmtZoneAreaDc3') { + head.title = '3단계'; + } else if (head.id === 'planSwageQntSumDc4' || head.id === 'swtrmtPpltnCntDc4' || head.id === 'swtrmtZoneAreaDc4') { + head.title = '4단계'; + } else if (head.id === 'planSwageQntSumDt4' || head.id === 'swtrmtPpltnCntDt4' || head.id === 'swtrmtZoneAreaDt4') { + head.title = '기승인\n4단계'; + } + }); + return item; + }) + ); + + const startIndex = (pageNo - 1) * pageSize; + const endIndex = startIndex + pageSize; + const paginatedData = enrichedData.slice(startIndex, endIndex); + + const fcltyCdList: string[] = []; + paginatedData.forEach(item => { + fcltyCdList.push(item.swtrmtFcltCd); }) - ); - const startIndex = (pageNo - 1) * pageSize; - const endIndex = startIndex + pageSize; - const paginatedData = enrichedData.slice(startIndex, endIndex); - - const fcltyCdList : string[]= []; - paginatedData.forEach(item => { - fcltyCdList.push(item.swtrmtFcltCd); - }) - - const detailFcltylist = await fetchFacilityDetailInfo(1, pageSize, fcltyCdList); - paginatedData.forEach(item => { - const filterdData = detailFcltylist.data.filter(fItem=>fItem.bscFctCd === item.swtrmtFcltCd)[0] || null; - item.fcltCpctSum = filterdData ? filterdData.fctCpc : ''; - item.location = filterdData ? filterdData.addr : ''; - item.ctpvNm = filterdData ? filterdData.sido : ''; - item.sggNm = filterdData ? filterdData.gugun : ''; - }); - return {data: paginatedData, count: enrichedData.length}; - }else{ - return {data: [], count: 0}; - } - } catch (error) { - console.error('시설계획정보를 가져오는데 실패했습니다:', error); - set((state) => ({ - ...state, - ProcessArea : { - ...state.ProcessArea, - isLoading : false + const detailFcltylist = await fetchFacilityDetailInfo(1, pageSize, fcltyCdList); + paginatedData.forEach(item => { + const filterdData = detailFcltylist.data.filter(fItem => fItem.bscFctCd === item.swtrmtFcltCd)[0] || null; + item.fcltCpctSum = filterdData ? filterdData.fctCpc : ''; + item.location = filterdData ? filterdData.addr : ''; + item.ctpvNm = filterdData ? filterdData.sido : ''; + item.sggNm = filterdData ? filterdData.gugun : ''; + item.trtDstr = filterdData ? filterdData.trtDstr : ''; + }); + return { data: paginatedData, count: enrichedData.length }; + } else { + return { data: [], count: 0 }; } - })); - return {data: [], count: 0}; - } + } catch (error) { + console.error('시설계획정보를 가져오는데 실패했습니다:', error); + set((state) => ({ + ...state, + ProcessArea: { + ...state.ProcessArea, + isLoading: false + } + })); + return { data: [], count: 0 }; + } }; /**==================================== * Store - expaort =====================================*/ export type ProcessAreaActions = { - searchProcessAreaList : (pageNo: number, pageSize: number, ctpvNm?:string, sggNm?:string, targetYear?:string) => void; - initModalData : (swtrmtFcltCd:string) => void; + searchProcessAreaList: (pageNo: number, pageSize: number, ctpvNm?: string, sggNm?: string, targetYear?: string) => void; + initModalData: (swtrmtFcltCd: string) => void; } export type ProcessAreaType = { ProcessAreaSearch: { isInit: boolean; - processFacilityLabelArray:string[]; + processFacilityLabelArray: string[]; }, - ProcessArea : { + ProcessArea: { isInit: boolean; upHeadList1: TableUpperProps[]; processAreaList: ProcessAreaProps[]; processAreaCount: number; excelList: ExcelType[]; isLoading: boolean; - }, + }, - ProcessAreaDetail : { + ProcessAreaDetail: { isInit: boolean; upHeadListFcltyImproveInfo: TableUpperProps[]; gridListFcltyImproveInfo: FcltyImproveInfoProps[]; // Changed from facilityAreaProps to ProcessAreaProps @@ -350,23 +352,23 @@ export const processAreaInitState: ProcessAreaType = { processFacilityLabelArray: ProcessFacilityLabelArray, }, - ProcessArea : { - isInit : false, - upHeadList1 : upHeadList1, - processAreaList : processAreaList, - processAreaCount : 0, - excelList : [], - isLoading : true, + ProcessArea: { + isInit: false, + upHeadList1: upHeadList1, + processAreaList: processAreaList, + processAreaCount: 0, + excelList: [], + isLoading: true, }, - ProcessAreaDetail : { - isInit : false, - upHeadListFcltyImproveInfo : upHeadListFcltyImproveInfo, - gridListFcltyImproveInfo : gridListFcltyImproveInfo, - upHeadListProcessPopulation : upHeadListProcessPopulation, - gridListProcessPopulation : gridListProcessPopulation, - upHeadListProcessAreaPlan : upHeadListProcessAreaPlan, - gridListProcessAreaPlan : gridListProcessAreaPlan, + ProcessAreaDetail: { + isInit: false, + upHeadListFcltyImproveInfo: upHeadListFcltyImproveInfo, + gridListFcltyImproveInfo: gridListFcltyImproveInfo, + upHeadListProcessPopulation: upHeadListProcessPopulation, + gridListProcessPopulation: gridListProcessPopulation, + upHeadListProcessAreaPlan: upHeadListProcessAreaPlan, + gridListProcessAreaPlan: gridListProcessAreaPlan, }, } @@ -375,14 +377,14 @@ type SetStateFunction = (state: ProcessAreaType | ((state: ProcessAreaType) => P export type ProcessAreaStore = ProcessAreaType & ProcessAreaActions; -export const processAreaReducer:(set:SetStateFunction)=>ProcessAreaActions=(set: SetStateFunction) => { +export const processAreaReducer: (set: SetStateFunction) => ProcessAreaActions = (set: SetStateFunction) => { return { /** * 시설정보 리스트 데이터 호출 * @param pageNo : 페이지 번호 * @param pageSize : 페이지 크기 */ - searchProcessAreaList: async ( pageNo:number, pageSize:number, ctpvNm?:string, sggNm?:string, targetYear?:string) => { + searchProcessAreaList: async (pageNo: number, pageSize: number, ctpvNm?: string, sggNm?: string, targetYear?: string) => { set((state: ProcessAreaType) => ({ ...state, ProcessArea: { @@ -391,12 +393,12 @@ export const processAreaReducer:(set:SetStateFunction)=>ProcessAreaActions=(set: }, })); const res = await fetchProcessAreaList(pageNo, pageSize, ctpvNm, sggNm, targetYear, set); - res.data.forEach((item:ProcessAreaProps) => { + res.data.forEach((item: ProcessAreaProps) => { item.fctCpc = item.fcltCpctSum; - DataForm({ type: 'float1', column: 'fctCpc', item:item as unknown as Record<string, unknown>}); + DataForm({ type: 'float1', column: 'fctCpc', item: item as unknown as Record<string, unknown> }); }) const excelDownList: ExcelType[] = []; - if(pageSize>5000){ + if (pageSize > 5000) { res.data.forEach(item => { item.fctCpc = item.fctCpc ? parseFloat(item.fctCpc.toString().replace(/,/g, '')) : ''; excelDownList.push({ @@ -425,78 +427,78 @@ export const processAreaReducer:(set:SetStateFunction)=>ProcessAreaActions=(set: 처리구역: item.trtDstr }); }); - }else{ + } else { res.data.forEach(item => { item.fctCpc = item.fctCpc ? parseFloat(item.fctCpc.toString().replace(/,/g, '')) : ''; }) } - set((state:ProcessAreaType) => ({ + set((state: ProcessAreaType) => ({ ...state, - ProcessArea: pageSize<5000?{ + ProcessArea: pageSize < 5000 ? { ...state.ProcessArea, processAreaList: res.data, processAreaCount: res.count, - isLoading : false, - }:{ + isLoading: false, + } : { ...state.ProcessArea, excelList: excelDownList, - isLoading : false, + isLoading: false, }, })) }, - initModalData: async (swtrmtFcltCd:string) => { + initModalData: async (swtrmtFcltCd: string) => { set((state: ProcessAreaType) => { const rtnData = state.ProcessArea.processAreaList.find((item: ProcessAreaProps) => item.swtrmtFcltCd === swtrmtFcltCd); if (rtnData) { - upHeadListFcltyImproveInfo.forEach((head:TableUpperProps) => { - if(head.id === 'planSwageQntSum' || head.id === 'swtrmtPpltnCnt' || head.id === 'swtrmtZoneArea') { - head.title = rtnData.planYr? '현재\n(' + rtnData.planYr + '년)':head.title = '현재'; - } else if(head.id === 'planSwageQntSumDc1' || head.id === 'swtrmtPpltnCntDc1' || head.id === 'swtrmtZoneAreaDc1') { - head.title = rtnData.planYrDc1? '1단계\n(' + rtnData.planYrDc1 + '년)':head.title = '1단계'; - } else if(head.id === 'planSwageQntSumDc2' || head.id === 'swtrmtPpltnCntDc2' || head.id === 'swtrmtZoneAreaDc2') { - head.title = rtnData.planYrDc2? '2단계\n(' + rtnData.planYrDc2 + '년)':head.title = '2단계'; - } else if(head.id === 'planSwageQntSumDc3' || head.id === 'swtrmtPpltnCntDc3' || head.id === 'swtrmtZoneAreaDc3') { - head.title = rtnData.planYrDc3? '3단계\n(' + rtnData.planYrDc3 + '년)':head.title = '3단계'; - } else if(head.id === 'planSwageQntSumDc4' || head.id === 'swtrmtPpltnCntDc4' || head.id === 'swtrmtZoneAreaDc4') { - head.title = rtnData.planYrDc4? '4단계\n(' + rtnData.planYrDc4 + '년)':head.title = '4단계'; - } else if(head.id === 'planSwageQntSumDt4' || head.id === 'swtrmtPpltnCntDt4' || head.id === 'swtrmtZoneAreaDt4') { - head.title = rtnData.planYrDt4? '기승인\n4단계(' + rtnData.planYrDt4 + '년)':head.title = '기승인\n4단계'; + upHeadListFcltyImproveInfo.forEach((head: TableUpperProps) => { + if (head.id === 'planSwageQntSum' || head.id === 'swtrmtPpltnCnt' || head.id === 'swtrmtZoneArea') { + head.title = rtnData.planYr ? '현재\n(' + rtnData.planYr + '년)' : head.title = '현재'; + } else if (head.id === 'planSwageQntSumDc1' || head.id === 'swtrmtPpltnCntDc1' || head.id === 'swtrmtZoneAreaDc1') { + head.title = rtnData.planYrDc1 ? '1단계\n(' + rtnData.planYrDc1 + '년)' : head.title = '1단계'; + } else if (head.id === 'planSwageQntSumDc2' || head.id === 'swtrmtPpltnCntDc2' || head.id === 'swtrmtZoneAreaDc2') { + head.title = rtnData.planYrDc2 ? '2단계\n(' + rtnData.planYrDc2 + '년)' : head.title = '2단계'; + } else if (head.id === 'planSwageQntSumDc3' || head.id === 'swtrmtPpltnCntDc3' || head.id === 'swtrmtZoneAreaDc3') { + head.title = rtnData.planYrDc3 ? '3단계\n(' + rtnData.planYrDc3 + '년)' : head.title = '3단계'; + } else if (head.id === 'planSwageQntSumDc4' || head.id === 'swtrmtPpltnCntDc4' || head.id === 'swtrmtZoneAreaDc4') { + head.title = rtnData.planYrDc4 ? '4단계\n(' + rtnData.planYrDc4 + '년)' : head.title = '4단계'; + } else if (head.id === 'planSwageQntSumDt4' || head.id === 'swtrmtPpltnCntDt4' || head.id === 'swtrmtZoneAreaDt4') { + head.title = rtnData.planYrDt4 ? '기승인\n4단계(' + rtnData.planYrDt4 + '년)' : head.title = '기승인\n4단계'; } }); - upHeadListProcessAreaPlan.forEach((head:TableUpperProps) => { - if(head.id === 'planSwageQntSum' || head.id === 'swtrmtPpltnCnt' || head.id === 'swtrmtZoneArea') { - head.title = rtnData.planYr? '현재\n(' + rtnData.planYr + '년)':head.title = '현재'; - } else if(head.id === 'planSwageQntSumDc1' || head.id === 'swtrmtPpltnCntDc1' || head.id === 'swtrmtZoneAreaDc1') { - head.title = rtnData.planYrDc1?'1단계\n(' + rtnData.planYrDc1 + '년)':head.title = '1단계'; - } else if(head.id === 'planSwageQntSumDc2' || head.id === 'swtrmtPpltnCntDc2' || head.id === 'swtrmtZoneAreaDc2') { - head.title = rtnData.planYrDc2?'2단계\n(' + rtnData.planYrDc2 + '년)':head.title = '2단계'; - } else if(head.id === 'planSwageQntSumDc3' || head.id === 'swtrmtPpltnCntDc3' || head.id === 'swtrmtZoneAreaDc3') { - head.title = rtnData.planYrDc3? '3단계\n(' + rtnData.planYrDc3 + '년)':head.title = '3단계'; - } else if(head.id === 'planSwageQntSumDc4' || head.id === 'swtrmtPpltnCntDc4' || head.id === 'swtrmtZoneAreaDc4') { - head.title = rtnData.planYrDc4?'4단계\n(' + rtnData.planYrDc4 + '년)':head.title = '4단계'; - } else if(head.id === 'planSwageQntSumDt4' || head.id === 'swtrmtPpltnCntDt4' || head.id === 'swtrmtZoneAreaDt4') { - head.title = rtnData.planYrDt4?'기승인\n4단계(' + rtnData.planYrDt4 + '년)':head.title = '기승인\n4단계'; + upHeadListProcessAreaPlan.forEach((head: TableUpperProps) => { + if (head.id === 'planSwageQntSum' || head.id === 'swtrmtPpltnCnt' || head.id === 'swtrmtZoneArea') { + head.title = rtnData.planYr ? '현재\n(' + rtnData.planYr + '년)' : head.title = '현재'; + } else if (head.id === 'planSwageQntSumDc1' || head.id === 'swtrmtPpltnCntDc1' || head.id === 'swtrmtZoneAreaDc1') { + head.title = rtnData.planYrDc1 ? '1단계\n(' + rtnData.planYrDc1 + '년)' : head.title = '1단계'; + } else if (head.id === 'planSwageQntSumDc2' || head.id === 'swtrmtPpltnCntDc2' || head.id === 'swtrmtZoneAreaDc2') { + head.title = rtnData.planYrDc2 ? '2단계\n(' + rtnData.planYrDc2 + '년)' : head.title = '2단계'; + } else if (head.id === 'planSwageQntSumDc3' || head.id === 'swtrmtPpltnCntDc3' || head.id === 'swtrmtZoneAreaDc3') { + head.title = rtnData.planYrDc3 ? '3단계\n(' + rtnData.planYrDc3 + '년)' : head.title = '3단계'; + } else if (head.id === 'planSwageQntSumDc4' || head.id === 'swtrmtPpltnCntDc4' || head.id === 'swtrmtZoneAreaDc4') { + head.title = rtnData.planYrDc4 ? '4단계\n(' + rtnData.planYrDc4 + '년)' : head.title = '4단계'; + } else if (head.id === 'planSwageQntSumDt4' || head.id === 'swtrmtPpltnCntDt4' || head.id === 'swtrmtZoneAreaDt4') { + head.title = rtnData.planYrDt4 ? '기승인\n4단계(' + rtnData.planYrDt4 + '년)' : head.title = '기승인\n4단계'; } }); - upHeadListProcessPopulation.forEach((head:TableUpperProps) => { - if(head.id === 'planSwageQntSum' || head.id === 'swtrmtPpltnCnt' || head.id === 'swtrmtZoneArea') { - head.title = rtnData.planYr?'현재\n(' + rtnData.planYr + '년)':head.title = '현재'; - } else if(head.id === 'planSwageQntSumDc1' || head.id === 'swtrmtPpltnCntDc1' || head.id === 'swtrmtZoneAreaDc1') { - head.title = rtnData.planYrDc1?'1단계\n(' + rtnData.planYrDc1 + '년)':head.title = '1단계'; - } else if(head.id === 'planSwageQntSumDc2' || head.id === 'swtrmtPpltnCntDc2' || head.id === 'swtrmtZoneAreaDc2') { - head.title = rtnData.planYrDc2?'2단계\n(' + rtnData.planYrDc2 + '년)':head.title = '2단계'; - } else if(head.id === 'planSwageQntSumDc3' || head.id === 'swtrmtPpltnCntDc3' || head.id === 'swtrmtZoneAreaDc3') { - head.title = rtnData.planYrDc3?'3단계\n(' + rtnData.planYrDc3 + '년)':head.title = '3단계'; - } else if(head.id === 'planSwageQntSumDc4' || head.id === 'swtrmtPpltnCntDc4' || head.id === 'swtrmtZoneAreaDc4') { - head.title = rtnData.planYrDc4?'4단계\n(' + rtnData.planYrDc4 + '년)':head.title = '4단계'; - } else if(head.id === 'planSwageQntSumDt4' || head.id === 'swtrmtPpltnCntDt4' || head.id === 'swtrmtZoneAreaDt4') { - head.title = rtnData.planYrDt4?'기승인\n4단계(' + rtnData.planYrDt4 + '년)':head.title = '기승인\n4단계'; + upHeadListProcessPopulation.forEach((head: TableUpperProps) => { + if (head.id === 'planSwageQntSum' || head.id === 'swtrmtPpltnCnt' || head.id === 'swtrmtZoneArea') { + head.title = rtnData.planYr ? '현재\n(' + rtnData.planYr + '년)' : head.title = '현재'; + } else if (head.id === 'planSwageQntSumDc1' || head.id === 'swtrmtPpltnCntDc1' || head.id === 'swtrmtZoneAreaDc1') { + head.title = rtnData.planYrDc1 ? '1단계\n(' + rtnData.planYrDc1 + '년)' : head.title = '1단계'; + } else if (head.id === 'planSwageQntSumDc2' || head.id === 'swtrmtPpltnCntDc2' || head.id === 'swtrmtZoneAreaDc2') { + head.title = rtnData.planYrDc2 ? '2단계\n(' + rtnData.planYrDc2 + '년)' : head.title = '2단계'; + } else if (head.id === 'planSwageQntSumDc3' || head.id === 'swtrmtPpltnCntDc3' || head.id === 'swtrmtZoneAreaDc3') { + head.title = rtnData.planYrDc3 ? '3단계\n(' + rtnData.planYrDc3 + '년)' : head.title = '3단계'; + } else if (head.id === 'planSwageQntSumDc4' || head.id === 'swtrmtPpltnCntDc4' || head.id === 'swtrmtZoneAreaDc4') { + head.title = rtnData.planYrDc4 ? '4단계\n(' + rtnData.planYrDc4 + '년)' : head.title = '4단계'; + } else if (head.id === 'planSwageQntSumDt4' || head.id === 'swtrmtPpltnCntDt4' || head.id === 'swtrmtZoneAreaDt4') { + head.title = rtnData.planYrDt4 ? '기승인\n4단계(' + rtnData.planYrDt4 + '년)' : head.title = '기승인\n4단계'; } }); - + // 시설증설 계획 데이터 생성 const fcltyImproveInfo: FcltyImproveInfoProps = { planSwageQntSumDt4: rtnData.planSwageQntSumDt4, diff --git a/apps/fo/app/store/processFacility/index.tsx b/apps/fo/app/store/processFacility/index.tsx index 8fa38e5bc..006185e6d 100644 --- a/apps/fo/app/store/processFacility/index.tsx +++ b/apps/fo/app/store/processFacility/index.tsx @@ -113,7 +113,10 @@ export type fcltyHistInfoProps = { sitArea: number, trtDstrArea: number, swrTrtPopl: number, - fctCpc: number | '' + fctCpc: number | '', + trtSrsHanNm?: string, // 공법구분 + trtMocHanNm?: string, // 공법계열 + trtFrfHanNm?: string, // 방법 } // 시설 수질정보 @@ -328,6 +331,9 @@ const upHeadListFcltyHistInfo: TableUpperProps[] = [ { id: "trlrnCmplDt", title: "시운전완료일" }, { id: "rnDt", title: "가동개시일" }, { id: "fctCpc", title: "시설용량(m³/일)", align: 'right' }, + { id: "trtSrsHanNm", title: "공법구분" }, + { id: "trtMocHanNm", title: "공법계열" }, + { id: "trtFrfHanNm", title: "방법" }, ]; // 시설 이력관리 (리스트) @@ -344,7 +350,10 @@ const gridListFcltyHistInfo: fcltyHistInfoProps[] = [ sitArea: 0, trtDstrArea: 0, swrTrtPopl: 0, - fctCpc: 860000 + fctCpc: 860000, + trtFrfHanNm: "", + trtMocHanNm: "", + trtSrsHanNm: "", } ]; @@ -418,7 +427,9 @@ const gridListFcltyWaterQltyInfo: fcltyWaterQltyInfoProps[] = [ */ export const fetchFacilityListInit = async (pageNo: number, pageSize: number, partValue?: string, nameCd?: string, fcltCpct?: string, oprtngSttsCd?: string, facilityPartValue?: string, facilityName?: string): Promise<{ data: facilityListProps[], count: number }> => { try { - const tailStr = partValue ? '/' + (partValue === 'G' ? 'A' : partValue) + '/' + nameCd + '/' + fcltCpct + '/' + oprtngSttsCd + '/' + facilityPartValue + (facilityName && facilityName !== '' ? '/' + facilityName : '') : ''; + const tailStr = partValue ? '/' + (partValue === 'G' ? 'A' : partValue) + '/' + nameCd + '/' + fcltCpct + '/' + oprtngSttsCd + + (facilityPartValue && facilityPartValue !== ''? '/' + facilityPartValue : '') + + (facilityName && facilityName !== ''? '/' + facilityName : '') : ''; const url = '/prcsFclt/info/' + pageNo + '/' + pageSize + tailStr; const res = await getFetch('fo', url); res.data.forEach((item: Record<string, unknown>) => { @@ -900,4 +911,4 @@ export const processFacilityReducer: (set: SetStateFunction) => ProcessFacilityA } } } -} \ No newline at end of file +} diff --git a/apps/fo/app/store/rdi/index.tsx b/apps/fo/app/store/rdi/index.tsx index 67c6efa1e..ff6a23053 100644 --- a/apps/fo/app/store/rdi/index.tsx +++ b/apps/fo/app/store/rdi/index.tsx @@ -322,15 +322,33 @@ export const rdiReducer:(set:SetStateFunction)=>RdiActions=(set: SetStateFunctio fcltCdList.push(item.bscFctCd); }); - const rdiiDetailListRes : any = await fetchFacilityRdiiDetailList(searchYear, fcltCdList); - console.log(rdiiDetailListRes); + // const rdiiDetailListRes : any = await fetchFacilityRdiiDetailList(searchYear, fcltCdList); + + //--------- + const rainDetailList: Record<string, any[]> = {}; + const results: any[] = []; + for (let idx = 0; idx < fcltCdList.length; idx += 100) { + const slicedItems = fcltCdList.slice(idx, idx + 100); + const result = await fetchFacilityRdiiDetailList(searchYear, slicedItems); + results.push(result); + } + //console.log(results); + // 결과를 합치기 + for (const res of results) { + if (res.status === 200 && res.data) { + for (const [key, value] of Object.entries(res.data)) { + const keyStr = String(key); + if (!rainDetailList[keyStr]) rainDetailList[keyStr] = []; + rainDetailList[keyStr].push(...(value as any[])); + } + } + } + //------- + // console.log(rdiiDetailListRes); for (const item of rtnData) { - //const fcltyCd = item.bscFctCd; - //const res: any = await fetchFacilityWaterQltyListHist(searchYear, fcltyCd); - //const rdiiDetailList = res.data.wqltObjs; - const rdiiDetailList = rdiiDetailListRes?.data[item.bscFctCd]; - //console.log(rdiiDetailListRes); + // const rdiiDetailList = rdiiDetailListRes?.data[item.bscFctCd]; + const rdiiDetailList = rainDetailList[item.bscFctCd]; let rtnWinterInflBod = 0; let rtnSummerBodInfl = 0; diff --git a/apps/fo/app/store/sewageOccur/index.tsx b/apps/fo/app/store/sewageOccur/index.tsx index e9857af71..fd219c0f1 100644 --- a/apps/fo/app/store/sewageOccur/index.tsx +++ b/apps/fo/app/store/sewageOccur/index.tsx @@ -4,6 +4,7 @@ import { roundNumber } from "@/app/components/common/DataForm"; import { OSU_PERCENT } from "../common"; import type { TableUpperProps } from "../dashboard"; import { fetchFacilityListInit } from "../processFacility"; +// import { fetchFacilityRainDetailList } from "../facilityPrecipitation"; export type { TableUpperProps }; /** @@ -29,7 +30,7 @@ export type SewageOccurProps = { mdmbsn: string, msrDt: string, } -export type modalTableType = { msrDt: string, waterUsage: number, swageChangePercent: number, estimationSewage: number }; +export type modalTableType = { msrDt: string, waterUsage: number, swageChangePercent: number, estimationSewage: number, } export type SewageOccurDetailProps = { cnt: number, sum: number, @@ -39,6 +40,8 @@ export type SewageOccurDetailProps = { sumWtrUsg: number, // 물사용량 합 avgWtrUsg: number, // 일평균 물사용량 monWtrUsg: number, // 월 물사용량 + maxEstSewerage: number, // 최대 오수발생량 추정 + minEstSewerage: number, // 최소 오수발생량 추정 } export type WtrUsgYmProps = { bscFctCd: string, // 처리구역시설코드 @@ -326,16 +329,52 @@ export const sewageOccurReducer: (set: any) => SewageOccurActions = (set: any) = }); }); - const soccurDetailRes: any = await fetchSweageOccurListFlow(searchYear, fcltCdList); - const wtrUsgYmRes: any = await fetchWtrUsgYmList(searchYear, fcltCdList); - // const wtrUsgMap : Record<string, number> = {}; - // wtrUsgRes.data.forEach(fclt => { - // wtrUsgMap[fclt.bscFctCd] = fclt.wtrUsg; - // }); + // const soccurDetailRes: any = await fetchSweageOccurListFlow(searchYear, fcltCdList); + // const wtrUsgYmRes: any = await fetchWtrUsgYmList(searchYear, fcltCdList); + + //--------- + const rainDetailList: Record<string, any[]> = {}; + const wtrDetailList: Record<string, any[]> = {}; + const results: any[] = []; + const wtrResults: any[] = []; + for (let idx = 0; idx < fcltCdList.length; idx += 100) { + const slicedItems = fcltCdList.slice(idx, idx + 100); + const result = await fetchSweageOccurListFlow(searchYear, slicedItems); + const wtrResult = await fetchWtrUsgYmList(searchYear, slicedItems); + results.push(result); + wtrResults.push(wtrResult); + } + //console.log(results); + // 결과를 합치기 + for (const res of results) { + if (res.status === 200 && res.data) { + for (const [key, value] of Object.entries(res.data)) { + const keyStr = String(key); + //rainDetailList[keyStr] = [...(rainDetailList[keyStr] || []), ...(value as any[])]; + if (!rainDetailList[keyStr]) rainDetailList[keyStr] = []; + rainDetailList[keyStr].push(...(value as any[])); + } + } + } + for (const res of wtrResults) { + if (res.status === 200 && res.data) { + for (const [key, value] of Object.entries(res.data)) { + const keyStr = String(key); + // wtrDetailList[keyStr] = [...(wtrDetailList[keyStr] || []), ...(value as any[])]; + if (!wtrDetailList[keyStr]) wtrDetailList[keyStr] = []; + wtrDetailList[keyStr].push(...(value as any[])); + } + } + } + //------- + + let maxEstimationSewage = 0, minEstimationSewage = 999999999; for (const item of sewageOccurPropsList) { - const detailList = soccurDetailRes?.data[item.bscFctCd]; - const wtrUsgYmList = wtrUsgYmRes?.data[item.bscFctCd]; + //const detailList = soccurDetailRes?.data[item.bscFctCd]; + // const wtrUsgYmList = wtrUsgYmRes?.data[item.bscFctCd]; + const detailList = rainDetailList[item.bscFctCd]; + const wtrUsgYmList = wtrDetailList[item.bscFctCd]; item.swageChangePercent = 90; let sumChungcunDay = 0; // 청천일수 @@ -367,18 +406,16 @@ export const sewageOccurReducer: (set: any) => SewageOccurActions = (set: any) = // console.log("totWaterUsage:", totWaterUsage); const count = detailList ? detailList.length : 0; - const avg = count == 0 ? 0 : totWaterUsage / count; // 일평균 유입량 - item.waterUsage = parseFloat((avg * 0.65).toFixed(2)); // 물사용량 = 일평균 유입량 * 65% + const avg = count == 0 ? 0 : totWaterUsage / count; // 연간 하수유입량(m³/일) + item.waterUsage = parseFloat((avg * 0.65).toFixed(2)); // 물사용량 = 연간 하수유입량(m³/일) * 65% : 실제 수용가의 물사용량 없는 경우 if (wtrUsgYearTotal > 0 && wtrUsgYearDays > 0) { - item.waterUsage = wtrUsgYearDays > 0 ? wtrUsgYearTotal / wtrUsgYearDays : 0; // 일평균 물사용량 + item.waterUsage = wtrUsgYearDays > 0 ? wtrUsgYearTotal / wtrUsgYearDays : 0; // 일평균 물사용량: 실제 수용가의 물사용량 } item.sewageInputYear = parseFloat(avg.toFixed(2)); // 연간 하수유입량(m³/일) item.sewageInputChungchun = parseFloat((chungcunSumFlow === 0 ? 0 : (chungcunSumFlow / sumChungcunDay)).toFixed(2)); // 청천시 하수유입량 item.estimationSewage = parseFloat((item.waterUsage * OSU_PERCENT).toFixed(2)); // 오수발생량 추정(m³/일) - if (item.bscFctCd === '11200PB001R') { - console.log('chungcunSumFlow:', chungcunSumFlow); - console.log('sumChungcunDay:', sumChungcunDay); - } + if (item.estimationSewage > maxEstimationSewage) maxEstimationSewage = item.estimationSewage; + if (item.estimationSewage < minEstimationSewage) minEstimationSewage = item.estimationSewage; // 반올림 처리 ["fctCpc", "waterUsage", "swageChangePercent", "estimationSewage", "sewageInputYear", "sewageInputChungchun"].forEach( (key) => (item[key] = roundNumber(item[key], 0)) @@ -427,93 +464,11 @@ export const sewageOccurReducer: (set: any) => SewageOccurActions = (set: any) = })); } - /* fetchSewageOccurList(pageNo, pageSize, searchYear, part, nameCd, fcltCpct, operationValue, fcltyNm).then(async (res) => { - const excelDownList: ExcelType[] = []; - const data = res.data; - // Promise.all을 사용하여 모든 비동기 작업이 완료될 때까지 기다림 - const calData = await Promise.all(data.map(async (item: SewageOccurProps) => { - item.sidoNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || ''; - item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || ''; - item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, '')); - item.swageChangePercent = 90; - - const bscFctCd = item.bscFctCd; - const res2 = await getFetch('fo', '/fclt/flow/' + bscFctCd + '/' + searchYear) as FlowResponse; - - let sumChungcunDay = 0; - let chungcunSumFlow = 0; - let totWaterUsage = 0; - - // 강우영향일 계산 (강우량이 3 초과이면 무조건 강우영향일) - const rainEffectData = CalcRainEffect(res2.data); - - rainEffectData.forEach((item: FlowDataItem) => { - - if(Number(item.rainEffectDay) === 0){ - chungcunSumFlow += Number(item.inflAmnt); - sumChungcunDay += 1; - } - - totWaterUsage += Number(item.inflAmnt); - }); - const avg = totWaterUsage /res2.count; - item.waterUsage = parseFloat((avg * 0.65).toFixed(2)); - item.sewageInputYear = parseFloat(avg.toFixed(2)); - item.sewageInputChungchun = parseFloat((chungcunSumFlow===0?0:(chungcunSumFlow / sumChungcunDay)).toFixed(2)); - item.estimationSewage = parseFloat((item.waterUsage * (item.swageChangePercent/100)).toFixed(2)); - return item; - })); - - calData.forEach((item: SewageOccurProps) => { - DataForm({ type: 'float0', column: 'fctCpc', item: item as unknown as Record<string, unknown> }); - DataForm({ type: 'float0', column: 'waterUsage', item: item as unknown as Record<string, unknown> }); - DataForm({ type: 'float0', column: 'swageChangePercent', item: item as unknown as Record<string, unknown> }); - DataForm({ type: 'float0', column: 'estimationSewage', item: item as unknown as Record<string, unknown> }); - DataForm({ type: 'float0', column: 'sewageInputYear', item: item as unknown as Record<string, unknown> }); - DataForm({ type: 'float0', column: 'sewageInputChungchun', item: item as unknown as Record<string, unknown> }); - }); - - if(pageSize>5000){ - calData.forEach((item: SewageOccurProps) => { - excelDownList.push({ - 시설코드: item.bscFctCd, - 권역: item.rvbsn, - 단위유역: item.lgbsn, - 중권역: item.mdmbsn, - 환경청: item.rgnNm, - 시설명: item.bscFctNm, - 시도: item.sidoNm, - 시군구: item.sggNm, - 주소: item.daddr, - "시설용량(m³/일)": item.fctCpc, - "물사용량(m³/일)": item.waterUsage, - "오수전환율(%)": item.swageChangePercent, - "오수발생량추정(m³/일)": item.estimationSewage, - "연간하수유입량(m³/일)": item.sewageInputYear, - "청천시하수유입량(m³/일)": item.sewageInputChungchun, - }); - }); - } - // 조회 데이터 설정 - set((state: any) => ({ - ...state, - SewageOccur: pageSize<5000?{ - ...state.SewageOccur, - sewageOccurList: calData, - sewageOccurCount: res.count, - isLoading : false, - }:{ - ...state.SewageOccur, - excelList: excelDownList, - isLoading : false, - }, - })) - }); */ }, searchSewageOccurDetailList: async (bscFctCd: string, searchYear: string, startYm?: string, endYm?: string) => { const fcltCdList = [bscFctCd]; - const wtrUsgRes: any = await fetchWtrUsgYmList(searchYear, fcltCdList); + const wtrUsgRes: any = await fetchWtrUsgYmList(searchYear, fcltCdList); // 수용가의 실제 물사용량 const res = await getFetch('fo', '/fclt/flow/' + bscFctCd + '/' + searchYear); const monthData: SewageOccurDetailProps[] = []; let tot = 0, cnt = 0; @@ -554,15 +509,15 @@ export const sewageOccurReducer: (set: any) => SewageOccurActions = (set: any) = } else { monthData[monthIdx] = { cnt: 1, sum: Number(item.inflAmnt), yr: item.msrDt.substring(0, 4) - , usage: 0, cntWtrUsg: 0, sumWtrUsg: 0, avgWtrUsg: 0, monWtrUsg: 0 + , usage: 0, cntWtrUsg: 0, sumWtrUsg: 0, avgWtrUsg: 0, monWtrUsg: 0, maxEstSewerage: 0, minEstSewerage: 999999999 }; } } - tot += Number(item.inflAmnt) + tot += Number(item.inflAmnt); // 연간 유입량 cnt++ }); - // 물사용량 + // 수용가 실제 물사용량 let totWtrUsg = 0; let cntWtrUsg = 0; if (wtrUsgRes && wtrUsgRes.data && wtrUsgRes.data[bscFctCd]) { @@ -582,25 +537,23 @@ export const sewageOccurReducer: (set: any) => SewageOccurActions = (set: any) = const tableData: modalTableType[] = [] - let maxWaterUsage = 0; - let minWaterUsage = 9999999999; + let maxWaterUsage = 0, minWaterUsage = 9999999999; + let maxEstSewage = 0, minEstSewage = 9999999999; let avgWaterUsage = 0; monthData.forEach((item, index) => { - item.avgWtrUsg = item.sum / item.cnt * 0.65; // 일평균 물사용량 = 일평균유입수 * 0.65 - if (item.sumWtrUsg > 0 && item.cntWtrUsg > 0) item.avgWtrUsg = item.sumWtrUsg / item.cntWtrUsg; + item.avgWtrUsg = item.sum / item.cnt * 0.65; // 일평균 물사용량 = 일평균유입수 * 0.65 : 수용가 실제 물사용량이 없으면 + if (item.sumWtrUsg > 0 && item.cntWtrUsg > 0) item.avgWtrUsg = item.sumWtrUsg / item.cntWtrUsg; //수용가 실제 물사용량이 있는 경우 + const estSewage = Math.round(item.avgWtrUsg * OSU_PERCENT * 1) / 1; // 오수발생량 추정 tableData.push({ msrDt: item.yr + '-' + (index + 1 >= 10 ? String(index + 1) : '0' + (index + 1)), waterUsage: Math.round(item.avgWtrUsg * 1) / 1, - swageChangePercent: OSU_PERCENT * 100,//(Math.round((item.usage/item.usage*OSU_PERCENT) * 100) / 100) * 100, - estimationSewage: Math.round(item.avgWtrUsg * OSU_PERCENT * 1) / 1 + swageChangePercent: OSU_PERCENT * 100, //오수전환율 (Math.round((item.usage/item.usage*OSU_PERCENT) * 100) / 100) * 100, + estimationSewage: estSewage, }) - if (item.avgWtrUsg > maxWaterUsage) { - maxWaterUsage = item.avgWtrUsg; - } - - if (item.avgWtrUsg < minWaterUsage) { - minWaterUsage = item.avgWtrUsg; - } + if (item.avgWtrUsg > maxWaterUsage) maxWaterUsage = item.avgWtrUsg; // 물사용량 최대 + if (item.avgWtrUsg < minWaterUsage) minWaterUsage = item.avgWtrUsg; // 물사용량 최소 + if (maxEstSewage < estSewage) maxEstSewage = estSewage; // 오수발생량 최대 + if (minEstSewage > estSewage) minEstSewage = estSewage; // 오수발생량 최소 }); avgWaterUsage = tot / cnt * 0.65 @@ -617,13 +570,13 @@ export const sewageOccurReducer: (set: any) => SewageOccurActions = (set: any) = msrDt: '최대', waterUsage: roundNumber(Math.round(maxWaterUsage * 100) / 100, 0), swageChangePercent: OSU_PERCENT * 100, - estimationSewage: roundNumber(Math.round(maxWaterUsage * OSU_PERCENT * 1) / 1, 0), + estimationSewage: roundNumber(maxEstSewage / 1, 0), }, { msrDt: '최소', waterUsage: roundNumber(Math.round(minWaterUsage * 100) / 100, 0), swageChangePercent: OSU_PERCENT * 100, - estimationSewage: roundNumber(Math.round(minWaterUsage * OSU_PERCENT * 1) / 1, 0), + estimationSewage: roundNumber(minEstSewage / 1, 0), }, ...tableData ] diff --git a/apps/fo/app/store/store.tsx b/apps/fo/app/store/store.tsx index f7f2222c6..c7064a40e 100644 --- a/apps/fo/app/store/store.tsx +++ b/apps/fo/app/store/store.tsx @@ -42,8 +42,9 @@ import { export type FoStore = CommonStore & tableType & DashBoardStore & ProcessFacilityStore & ProcessAreaStore & FlowRateSearchStore & FacilityPrecipitationStore & FacilityWaterQltyStore & PlanSewageStore & SewageOccurStore & UnderWaterStore & RdiStore; export type FoState = CommonType & tableState & ProcessFacilityType & ProcessAreaType & FacilityPrecipitationType & FlowRateSearchType & DashBoardType & FacilityWaterQltyType & PlanSewageType & SewageOccurType & UnderWaterType & RdiType; -export const StoreZus = - (initState: FoState = { ...commonState, +export const StoreZus = + (initState: FoState = { + ...commonState, ...tableStateExport , ...processFacilityInitState , ...processAreaInitState @@ -57,22 +58,22 @@ export const StoreZus = , ...rdiInitState } ) => { - return createStore<FoStore>()((set) => { - return { - ...initState, - ...CommonReducer(set), - ...tableActionsExport(set), - ...dashBoardReducer(set), - ...processFacilityReducer(set), - ...processAreaReducer(set), - ...facilityPrecipitationReducer(set), - ...flowRateSearchReducer(set), - ...facilityWaterQltyReducer(set), - ...planSewageReducer(set), - ...sewageOccurReducer(set), - ...underWaterReducer(set), - ...rdiReducer(set), - } - }) -} + return createStore<FoStore>()((set, get) => { + return { + ...initState, + ...CommonReducer(set), + ...tableActionsExport(set), + ...dashBoardReducer(set, get), + ...processFacilityReducer(set), + ...processAreaReducer(set), + ...facilityPrecipitationReducer(set), + ...flowRateSearchReducer(set), + ...facilityWaterQltyReducer(set), + ...planSewageReducer(set), + ...sewageOccurReducer(set), + ...underWaterReducer(set), + ...rdiReducer(set), + } + }) + } //store 등록하는 부분 \ No newline at end of file diff --git a/apps/fo/app/store/underWaterInput/index.tsx b/apps/fo/app/store/underWaterInput/index.tsx index c6a2167d8..806b12e2b 100644 --- a/apps/fo/app/store/underWaterInput/index.tsx +++ b/apps/fo/app/store/underWaterInput/index.tsx @@ -5,20 +5,21 @@ import type { ExcelType, UnderWaterInputModalProps, UnderWaterProps, WtrUsgYmPro // import CalcRainEffect from "@/app/components/common/CalcRainEffect"; import { OSU_PERCENT } from "../common"; import { fetchFacilityListInit } from "../processFacility"; +import { getDaysInMonth } from "@/app/components/common/DataForm"; export type { TableUpperProps }; // SewageOccurDetailProps 타입을 여기에 정의 export type SewageOccurDetailProps = { - cnt : number, - sum : number, - yr:string, - usage:number, // 일평균 유입량 - sunnyCnt : number, - sunnyInflAmnt : number, - sunnyAvgInflAmnt : number, // 청천시 일평균 유입량 - cntWtrUsg : number, // 물사용량 일자수 - sumWtrUsg : number, // 물사용량 합 - avgWtrUsg:number, // 일평균 물사용량 + cnt: number, + sum: number, + yr: string, + usage: number, // 일평균 유입량 + sunnyCnt: number, + sunnyInflAmnt: number, + sunnyAvgInflAmnt: number, // 청천시 일평균 유입량 + cntWtrUsg: number, // 물사용량 일자수 + sumWtrUsg: number, // 물사용량 합 + avgWtrUsg: number, // 일평균 물사용량 } /**==================================== * Store - 타입 @@ -35,145 +36,145 @@ export type SewageOccurDetailProps = { /** * (1) 메인페이지 - */ + */ // 검색단 -const LabelArray:string[] = ['구분', '시도', '시군구','용량별', '시설명', '조회기간', '시설상태', '권역', '단위유역', '중권역', '환경청별']; +const LabelArray: string[] = ['구분', '시도', '시군구', '용량별', '시설명', '조회기간', '시설상태', '권역', '단위유역', '중권역', '환경청별']; // 리스트 헤더 -const upHeadList1:TableUpperProps[] = [ - { id: "A", title: "행정구역", row:0, colSpan:2 }, - { id: "sidoNm", title: "시도", row:1, widthPercent: 8 }, - { id: "sggNm", title: "시군구", row:1, widthPercent: 8 }, - { id: "bscFctNm", title: "시설명", row:0, rowSpan:2, widthPercent: 8 }, - { id: "daddr", title: "주소", row:0, rowSpan:2 }, - { id: "fctCpc", title: "시설용량(m³/일)", align:"right", row:0, rowSpan:2, widthPercent: 8 }, - { id: "inflAmnt", title: "연평균유입하수량(m³/일)", align:"right", row:0, rowSpan:2, widthPercent: 10 }, - { id: "C", title: "지하수 유입량 추정", align:"right", row:0, colSpan:3 }, - { id: "underWaterInputYear", title: "연간\n(m³/일)", align:"right", row:1, widthPercent: 8 }, - { id: "underWaterInputSummer", title: "하절기\n(m³/일)", align:"right", row:1, widthPercent: 8 }, - { id: "underWaterInputWinter", title: "동절기\n(m³/일)", align:"right", row:1, widthPercent: 8 }, - { id: "properYn", title: "적정여부", row:0, rowSpan:2, widthPercent: 8 }, +const upHeadList1: TableUpperProps[] = [ + { id: "A", title: "행정구역", row: 0, colSpan: 2 }, + { id: "sidoNm", title: "시도", row: 1, widthPercent: 8 }, + { id: "sggNm", title: "시군구", row: 1, widthPercent: 8 }, + { id: "bscFctNm", title: "시설명", row: 0, rowSpan: 2, widthPercent: 8 }, + { id: "daddr", title: "주소", row: 0, rowSpan: 2 }, + { id: "fctCpc", title: "시설용량(m³/일)", align: "right", row: 0, rowSpan: 2, widthPercent: 8 }, + { id: "inflAmnt", title: "연평균유입하수량(m³/일)", align: "right", row: 0, rowSpan: 2, widthPercent: 10 }, + { id: "C", title: "지하수 유입량 추정", align: "right", row: 0, colSpan: 3 }, + { id: "underWaterInputYear", title: "연간\n(m³/일)", align: "right", row: 1, widthPercent: 8 }, + { id: "underWaterInputSummer", title: "하절기\n(m³/일)", align: "right", row: 1, widthPercent: 8 }, + { id: "underWaterInputWinter", title: "동절기\n(m³/일)", align: "right", row: 1, widthPercent: 8 }, + { id: "properYn", title: "적정여부", row: 0, rowSpan: 2, widthPercent: 8 }, ]; -const upHeadList2:TableUpperProps[] = [ - { id: "A", title: "유역", row:0, colSpan:3 }, - { id: "rvbsn", title: "권역", row:1, widthPercent: 5 }, - { id: "lgbsn", title: "단위유역", row:1, widthPercent: 5 }, - { id: "mdmbsn", title: "중권역", row:1, widthPercent: 5 }, - { id: "bscFctNm", title: "시설명", row:0, rowSpan:2, widthPercent: 8 }, - { id: "daddr", title: "주소", row:0, rowSpan:2 }, - { id: "fctCpc", title: "시설용량(m³/일)", align:"right", row:0, rowSpan:2, widthPercent: 8 }, - { id: "inflAmnt", title: "연평균유입하수량(m³/일)", align:"right", row:0, rowSpan:2, widthPercent: 10 }, - { id: "C", title: "지하수 유입량 추정", align:"right", row:0, colSpan:3 }, - { id: "underWaterInputYear", title: "연간\n(m³/일)", align:"right", row:1, widthPercent: 8 }, - { id: "underWaterInputSummer", title: "하절기\n(m³/일)", align:"right", row:1, widthPercent: 8 }, - { id: "underWaterInputWinter", title: "동절기\n(m³/일)", align:"right", row:1, widthPercent: 8 }, - { id: "properYn", title: "적정여부", row:0, rowSpan:2, widthPercent: 8 }, +const upHeadList2: TableUpperProps[] = [ + { id: "A", title: "유역", row: 0, colSpan: 3 }, + { id: "rvbsn", title: "권역", row: 1, widthPercent: 5 }, + { id: "lgbsn", title: "단위유역", row: 1, widthPercent: 5 }, + { id: "mdmbsn", title: "중권역", row: 1, widthPercent: 5 }, + { id: "bscFctNm", title: "시설명", row: 0, rowSpan: 2, widthPercent: 8 }, + { id: "daddr", title: "주소", row: 0, rowSpan: 2 }, + { id: "fctCpc", title: "시설용량(m³/일)", align: "right", row: 0, rowSpan: 2, widthPercent: 8 }, + { id: "inflAmnt", title: "연평균유입하수량(m³/일)", align: "right", row: 0, rowSpan: 2, widthPercent: 10 }, + { id: "C", title: "지하수 유입량 추정", align: "right", row: 0, colSpan: 3 }, + { id: "underWaterInputYear", title: "연간\n(m³/일)", align: "right", row: 1, widthPercent: 8 }, + { id: "underWaterInputSummer", title: "하절기\n(m³/일)", align: "right", row: 1, widthPercent: 8 }, + { id: "underWaterInputWinter", title: "동절기\n(m³/일)", align: "right", row: 1, widthPercent: 8 }, + { id: "properYn", title: "적정여부", row: 0, rowSpan: 2, widthPercent: 8 }, ]; -const upHeadList3:TableUpperProps[] = [ - { id: "A", title: "유역", row:0, colSpan:3 }, - { id: "rvbsn", title: "권역", upName:"upChangeRe1", upSequnce:1, row:1, widthPercent: 5 }, - { id: "lgbsn", title: "단위유역", upName:"upChangeRe1", upSequnce:2, row:1, widthPercent: 5 }, - { id: "mdmbsn", title: "중권역", upName:"upChangeRe1", upSequnce:3, row:1, widthPercent: 5 }, - { id: "rgnNm", title: "환경청", row:0, rowSpan:2, widthPercent: 8 }, - { id: "B", title: "행정구역", row:0, colSpan:2 }, - { id: "sidoNm", title: "시도", row:1, widthPercent: 6 }, - { id: "sggNm", title: "시군구", row:1, widthPercent: 6 }, - { id: "bscFctNm", title: "시설명", row:0, rowSpan:2, widthPercent: 8 }, - { id: "daddr", title: "주소", row:0, rowSpan:2 }, - { id: "fctCpc", title: "시설용량(m³/일)", align:"right", row:0, rowSpan:2, widthPercent: 6 }, - { id: "inflAmnt", title: "연평균유입하수량(m³/일)", align:"right", row:0, rowSpan:2, widthPercent: 10 }, - { id: "C", title: "지하수 유입량 추정", align:"right", row:0, colSpan:3 }, - { id: "underWaterInputYear", title: "연간\n(m³/일)", align:"right", row:1, widthPercent: 6}, - { id: "underWaterInputSummer", title: "하절기\n(m³/일)", align:"right", row:1, widthPercent: 6 }, - { id: "underWaterInputWinter", title: "동절기\n(m³/일)", align:"right", row:1, widthPercent: 6 }, - { id: "properYn", title: "적정여부", row:0, rowSpan:2, widthPercent: 6 }, +const upHeadList3: TableUpperProps[] = [ + { id: "A", title: "유역", row: 0, colSpan: 3 }, + { id: "rvbsn", title: "권역", upName: "upChangeRe1", upSequnce: 1, row: 1, widthPercent: 5 }, + { id: "lgbsn", title: "단위유역", upName: "upChangeRe1", upSequnce: 2, row: 1, widthPercent: 5 }, + { id: "mdmbsn", title: "중권역", upName: "upChangeRe1", upSequnce: 3, row: 1, widthPercent: 5 }, + { id: "rgnNm", title: "환경청", row: 0, rowSpan: 2, widthPercent: 8 }, + { id: "B", title: "행정구역", row: 0, colSpan: 2 }, + { id: "sidoNm", title: "시도", row: 1, widthPercent: 6 }, + { id: "sggNm", title: "시군구", row: 1, widthPercent: 6 }, + { id: "bscFctNm", title: "시설명", row: 0, rowSpan: 2, widthPercent: 8 }, + { id: "daddr", title: "주소", row: 0, rowSpan: 2 }, + { id: "fctCpc", title: "시설용량(m³/일)", align: "right", row: 0, rowSpan: 2, widthPercent: 6 }, + { id: "inflAmnt", title: "연평균유입하수량(m³/일)", align: "right", row: 0, rowSpan: 2, widthPercent: 10 }, + { id: "C", title: "지하수 유입량 추정", align: "right", row: 0, colSpan: 3 }, + { id: "underWaterInputYear", title: "연간\n(m³/일)", align: "right", row: 1, widthPercent: 6 }, + { id: "underWaterInputSummer", title: "하절기\n(m³/일)", align: "right", row: 1, widthPercent: 6 }, + { id: "underWaterInputWinter", title: "동절기\n(m³/일)", align: "right", row: 1, widthPercent: 6 }, + { id: "properYn", title: "적정여부", row: 0, rowSpan: 2, widthPercent: 6 }, ]; // 모달 -const upHeadListUnderWaterDetail:TableUpperProps[] = [ +const upHeadListUnderWaterDetail: TableUpperProps[] = [ { id: "msrDt", title: "날짜" }, - { id: "realSewageInput", title: "청천시 실제 하수유입량\n(m³/일)", align:"right" }, - { id: "sewageOccur", title: "오수발생량\n(m³/일)", align:"right" }, - { id: "underWaterInput", title: "지하수 유입량\n(m³/일)", align:"right" }, + { id: "realSewageInput", title: "청천시 실제 하수유입량\n(m³/일)", align: "right" }, + { id: "sewageOccur", title: "오수발생량\n(m³/일)", align: "right" }, + { id: "underWaterInput", title: "지하수 유입량\n(m³/일)", align: "right" }, ]; -const underWaterList:UnderWaterProps[] = [ +const underWaterList: UnderWaterProps[] = [ ] -const underWaterDetailList:UnderWaterInputModalProps[] = [ +const underWaterDetailList: UnderWaterInputModalProps[] = [ { - msrDt : "2024-01", - realSewageInput : 178007, - sewageOccur : 10.5, - underWaterInput : 80.6, + msrDt: "2024-01", + realSewageInput: 178007, + sewageOccur: 10.5, + underWaterInput: 80.6, }, { - msrDt : "2024-02", - realSewageInput : 123007, - sewageOccur : 10.7, - underWaterInput : 80.6, + msrDt: "2024-02", + realSewageInput: 123007, + sewageOccur: 10.7, + underWaterInput: 80.6, }, { - msrDt : "2024-03", - realSewageInput : 213007, - sewageOccur : 10.1, - underWaterInput : 80.6, + msrDt: "2024-03", + realSewageInput: 213007, + sewageOccur: 10.1, + underWaterInput: 80.6, }, { - msrDt : "2024-04", - realSewageInput : 83007, - sewageOccur : 9.57, - underWaterInput : 80.6, + msrDt: "2024-04", + realSewageInput: 83007, + sewageOccur: 9.57, + underWaterInput: 80.6, }, { - msrDt : "2024-05", - realSewageInput : 15007, - sewageOccur : 3.57, - underWaterInput : 80.6, + msrDt: "2024-05", + realSewageInput: 15007, + sewageOccur: 3.57, + underWaterInput: 80.6, }, { - msrDt : "2024-06", - realSewageInput : 123007, - sewageOccur : '', - underWaterInput : 80.6, + msrDt: "2024-06", + realSewageInput: 123007, + sewageOccur: '', + underWaterInput: 80.6, }, { - msrDt : "2024-07", - realSewageInput : 101007, - sewageOccur : 1, - underWaterInput : 80.6, + msrDt: "2024-07", + realSewageInput: 101007, + sewageOccur: 1, + underWaterInput: 80.6, }, { - msrDt : "2024-08", - realSewageInput : 103807, - sewageOccur : 10.57, - underWaterInput : '', + msrDt: "2024-08", + realSewageInput: 103807, + sewageOccur: 10.57, + underWaterInput: '', }, { - msrDt : "2024-09", - realSewageInput : 9007, - sewageOccur : 10.57, - underWaterInput : 80.6, + msrDt: "2024-09", + realSewageInput: 9007, + sewageOccur: 10.57, + underWaterInput: 80.6, }, { - msrDt : "2024-10", - realSewageInput : 163007, - sewageOccur : 10.57, - underWaterInput : 80.6, + msrDt: "2024-10", + realSewageInput: 163007, + sewageOccur: 10.57, + underWaterInput: 80.6, }, { - msrDt : "2024-11", - realSewageInput : 123007, - sewageOccur : 10.57, - underWaterInput : 80.6, + msrDt: "2024-11", + realSewageInput: 123007, + sewageOccur: 10.57, + underWaterInput: 80.6, }, { - msrDt : "2024-12", - realSewageInput : 1007, - sewageOccur : 10.57, - underWaterInput : 80.6, + msrDt: "2024-12", + realSewageInput: 1007, + sewageOccur: 10.57, + underWaterInput: 80.6, }, ] /**==================================== @@ -185,8 +186,8 @@ export interface FlowDataItem { inflAmnt: number | string; infoRnDay: number; msrDt: string; - rainEffectDay? : number; - sumFlow? : number | string | ''; + rainEffectDay?: number; + sumFlow?: number | string | ''; } export interface ApiResponse { @@ -216,9 +217,9 @@ const fetchUnderWaterList = async (pageNo: number, pageSize: number, searchYear: // } // 시설들 유량 정보 조회 -export const fetchDetailListFlow = async (searchYear: string|undefined, fcltCdList: string[]): Promise<UnderWaterProps | null> => { +export const fetchDetailListFlow = async (searchYear: string | undefined, fcltCdList: string[]): Promise<UnderWaterProps | null> => { try { - const res = await postFetch('fo', '/fclt/flow/'+searchYear, fcltCdList); + const res = await postFetch('fo', '/fclt/flow/' + searchYear, fcltCdList); return res; } catch (error) { console.error('시설정보 오수발생량 상세 데이터를 가져오는데 실패했습니다:', error); @@ -226,9 +227,9 @@ export const fetchDetailListFlow = async (searchYear: string|undefined, fcltCdLi } }; // 시설들 월별 물사용량 정보 조회 -export const fetchWtrUsgYmList = async (searchYear: string|undefined, fcltCdList: string[]): Promise<WtrUsgYmProps | null> => { +export const fetchWtrUsgYmList = async (searchYear: string | undefined, fcltCdList: string[]): Promise<WtrUsgYmProps | null> => { try { - const res = await postFetch('fo', '/fclt/flow/wtrUsgYm/'+searchYear, fcltCdList); + const res = await postFetch('fo', '/fclt/flow/wtrUsgYm/' + searchYear, fcltCdList); return res; } catch (error) { console.error('시설들 월별 물사용량 데이터를 가져오는데 실패했습니다:', error); @@ -241,16 +242,16 @@ export const fetchWtrUsgYmList = async (searchYear: string|undefined, fcltCdList ====================================*/ export type UnderWaterActions = { searchUnderWaterList: (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => void; - unserWaterModal:(bscFctCd:string, searchYear:string, startYm:string, endYm:string)=>void + underWaterModal: (bscFctCd: string, searchYear: string, startYm: string, endYm: string) => void } export type UnderWaterType = { UnderWaterSearch: { isInit: boolean; - labelArray:string[]; + labelArray: string[]; }, - UnderWater : { + UnderWater: { isInit: boolean; upHeadList1: TableUpperProps[]; upHeadList2: TableUpperProps[]; @@ -261,7 +262,7 @@ export type UnderWaterType = { isLoading: boolean; }, - UnderWaterDetail : { + UnderWaterDetail: { isInit: boolean; upHeadListUnderWaterDetail: TableUpperProps[]; gridListUnderWaterDetail: UnderWaterInputModalProps[]; @@ -274,30 +275,30 @@ export const underWaterInitState: UnderWaterType = { labelArray: LabelArray, }, - UnderWater : { - isInit : false, - upHeadList1 : upHeadList1, - upHeadList2 : upHeadList2, - upHeadList3 : upHeadList3, - underWaterList : underWaterList, - underWaterCount : 4, - excelList : [], - isLoading : true, + UnderWater: { + isInit: false, + upHeadList1: upHeadList1, + upHeadList2: upHeadList2, + upHeadList3: upHeadList3, + underWaterList: underWaterList, + underWaterCount: 4, + excelList: [], + isLoading: true, }, - UnderWaterDetail : { + UnderWaterDetail: { isInit: false, upHeadListUnderWaterDetail: upHeadListUnderWaterDetail, gridListUnderWaterDetail: underWaterDetailList, } } -const dataFormSetting=(item : UnderWaterProps)=> { +const dataFormSetting = (item: UnderWaterProps) => { DataForm({ type: 'float0', column: 'fctCpc', item: item as unknown as Record<string, unknown> }); DataForm({ type: 'float0', column: 'inflAmnt', item: item as unknown as Record<string, unknown> }); - DataForm({ type: 'float0', column: 'underWaterInputYear', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float0', column: 'underWaterInputSummer', item: item as unknown as Record<string, unknown>}); - DataForm({ type: 'float0', column: 'underWaterInputWinter', item: item as unknown as Record<string, unknown>}); + DataForm({ type: 'float0', column: 'underWaterInputYear', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float0', column: 'underWaterInputSummer', item: item as unknown as Record<string, unknown> }); + DataForm({ type: 'float0', column: 'underWaterInputWinter', item: item as unknown as Record<string, unknown> }); } // 병렬 처리 제한용 헬퍼 @@ -310,47 +311,34 @@ const dataFormSetting=(item : UnderWaterProps)=> { // } // return results; // } -/** - * yyyymm 형태의 문자열을 받아 해당 월의 일수를 반환 - * @param {string} yyyymm - 예: "202502", "202412" - * @returns {number} 해당 월의 일수 (예: 28, 29, 30, 31) - */ -function getDaysInMonth(yyyymm) { - if (typeof yyyymm !== 'string' || yyyymm.length !== 6) { - throw new Error("입력은 'yyyymm' 형식의 문자열이어야 합니다. 예: '202502'"); - } - - const year = parseInt(yyyymm.slice(0, 4), 10); // "2025" -> 2025 - const month = parseInt(yyyymm.slice(4, 6), 10); // "02" -> 2 - - if (isNaN(year) || isNaN(month) || month < 1 || month > 12) { - throw new Error("유효한 년/월이 아닙니다."); - } - - // Date(year, month, 0) -> 전달의 마지막 날 - // month에 '다음달'을 넣고, 일(day)에 0을 주면 해당 월의 마지막 날짜가 됨 - return new Date(year, month, 0).getDate(); -} export type UnderWaterStore = UnderWaterType & UnderWaterActions; -export const underWaterReducer:(set:(fn: (state: UnderWaterType) => UnderWaterType) => void)=>UnderWaterActions=(set: (fn: (state: UnderWaterType) => UnderWaterType) => void) => { +export const underWaterReducer: (set: (fn: (state: UnderWaterType) => UnderWaterType) => void) => UnderWaterActions = (set: (fn: (state: UnderWaterType) => UnderWaterType) => void) => { return { searchUnderWaterList: async (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => { + set((state: UnderWaterType) => ({ + ...state, + UnderWater: { + ...state.UnderWater, + isLoading: true, + }, + })); + const facilityInitData = await fetchFacilityListInit(pageNo, pageSize, part, nameCd, fcltCpct, operationValue, fcltyNm); - - if(pageSize < 5000){ + + if (pageSize < 5000) { set((state: UnderWaterType) => ({ ...state, UnderWater: { ...state.UnderWater, - underWaterList : facilityInitData.data, - underWaterCount : facilityInitData.count, - isInit : true, + underWaterList: facilityInitData.data, + underWaterCount: facilityInitData.count, + isInit: true, isLoading: true, }, })); - }else{ + } else { set((state: UnderWaterType) => ({ ...state, UnderWater: { @@ -359,43 +347,79 @@ export const underWaterReducer:(set:(fn: (state: UnderWaterType) => UnderWaterTy }, })); } - + const fcltylist = facilityInitData.data; - const fcltCdList : string[] = []; // 시설코드목록 - const underWaterPropsList : UnderWaterProps[] = []; // 오수발생량시설목록 + const fcltCdList: string[] = []; // 시설코드목록 + const underWaterPropsList: UnderWaterProps[] = []; // 오수발생량시설목록 fcltylist.forEach(item => { fcltCdList.push(item.bscFctCd); underWaterPropsList.push({ - bscFctCd : item.bscFctCd, - sidoNm : item.sido, - sggNm : item.gugun, - lclgvrnmntNm : '', - bscFctNm : item.bscFctNm, - daddr : item.daddr, - fctCpc : parseFloat(item.fctCpc.toString().replace(/,/g, '')), - inflAmnt : 0, - waterUsage : 0, - underWaterInputYear : 0, - underWaterInputSummer : 0, - underWaterInputWinter : 0, - properYn : '', - infoRnDay : 0, - rgnNm : item.rgnNm, - rvbsn : item.rvbsn, - lgbsn : item.lgbsn, - mdmbsn : item.mdmbsn, - msrDt : '', + bscFctCd: item.bscFctCd, + sidoNm: item.sido, + sggNm: item.gugun, + lclgvrnmntNm: '', + bscFctNm: item.bscFctNm, + daddr: item.daddr, + fctCpc: parseFloat(item.fctCpc.toString().replace(/,/g, '')), + inflAmnt: 0, + waterUsage: 0, + underWaterInputYear: 0, + underWaterInputSummer: 0, + underWaterInputWinter: 0, + properYn: '', + infoRnDay: 0, + rgnNm: item.rgnNm, + rvbsn: item.rvbsn, + lgbsn: item.lgbsn, + mdmbsn: item.mdmbsn, + msrDt: '', }); }); - - const soccurDetailRes : any = await fetchDetailListFlow(searchYear, fcltCdList); - const wtrUsgYmRes : any = await fetchWtrUsgYmList(searchYear, fcltCdList); - + + // const soccurDetailRes: any = await fetchDetailListFlow(searchYear, fcltCdList); + // const wtrUsgYmRes: any = await fetchWtrUsgYmList(searchYear, fcltCdList); + //--------- + const rainDetailList: Record<string, any[]> = {}; + const wtrDetailList: Record<string, any[]> = {}; + const results: any[] = []; + const wtrResults: any[] = []; + for (let idx = 0; idx < fcltCdList.length; idx += 100) { + const slicedItems = fcltCdList.slice(idx, idx + 100); + const result = await fetchDetailListFlow(searchYear, slicedItems); + const wtrResult = await fetchWtrUsgYmList(searchYear, slicedItems); + results.push(result); + wtrResults.push(wtrResult); + } + //console.log(results); + // 결과를 합치기 + for (const res of results) { + if (res.status === 200 && res.data) { + for (const [key, value] of Object.entries(res.data)) { + const keyStr = String(key); + //rainDetailList[keyStr] = [...(rainDetailList[keyStr] || []), ...(value as any[])]; + if (!rainDetailList[keyStr]) rainDetailList[keyStr] = []; + rainDetailList[keyStr].push(...(value as any[])); + } + } + } + for (const res of wtrResults) { + if (res.status === 200 && res.data) { + for (const [key, value] of Object.entries(res.data)) { + const keyStr = String(key); + // wtrDetailList[keyStr] = [...(wtrDetailList[keyStr] || []), ...(value as any[])]; + if (!wtrDetailList[keyStr]) wtrDetailList[keyStr] = []; + wtrDetailList[keyStr].push(...(value as any[])); + } + } + } + //------- for (const item of underWaterPropsList) { - - const detailList = soccurDetailRes?.data[item.bscFctCd]; - const wtrUsgYmList = wtrUsgYmRes?.data[item.bscFctCd]; - + + // const detailList = soccurDetailRes?.data[item.bscFctCd]; + // const wtrUsgYmList = wtrUsgYmRes?.data[item.bscFctCd]; + const detailList = rainDetailList[item.bscFctCd]; + const wtrUsgYmList = wtrDetailList[item.bscFctCd]; + let chungcunSumFlow = 0; // 청천유입량 let sumChungcunDay = 0; // 청천일수 let sumSummerChungcunDay = 0; // 하절기 청천일수 @@ -407,39 +431,39 @@ export const underWaterReducer:(set:(fn: (state: UnderWaterType) => UnderWaterTy let totWinterUsage = 0; // 동절기 하수유입량 let totSummerDays = 0; // 하절기 일수 let totWinterDays = 0; // 동절기 일수 - + const summerMonth = ['06', '07', '08']; const winterMonth = ['01', '02']; - + let rainEffectDay = 0; detailList?.forEach(item2 => { if (item2.infoRnDay == null || item2.infoRnDay.trim() == 'x') item2.infoRnDay = 0; - if(Number(item2.infoRnDay) >= 3) { + if (Number(item2.infoRnDay) >= 3) { rainEffectDay = 2; - }else{ + } else { rainEffectDay--; } - if(rainEffectDay<0){ //청천시 + if (rainEffectDay < 0) { //청천시 chungcunSumFlow += Number(item2.inflAmnt); sumChungcunDay += 1; - if(item2.msrDt.substring(4,6) ==='06' || item2.msrDt.substring(4,6) ==='07' || item2.msrDt.substring(4,6) ==='08'){ + if (item2.msrDt.substring(4, 6) === '06' || item2.msrDt.substring(4, 6) === '07' || item2.msrDt.substring(4, 6) === '08') { // 하절기 6,7,8월 sumSummerChungcunDay += 1; sumSummerChungcunFlow += Number(item2.inflAmnt); //item2.inflAmnt && item2.inflAmnt !== 'x' ? Number(item2.inflAmnt.toString().trim()) : 0; - }else if(item2.msrDt.substring(4,6) ==='01' || item2.msrDt.substring(4,6) ==='02'){ + } else if (item2.msrDt.substring(4, 6) === '01' || item2.msrDt.substring(4, 6) === '02') { // 동절기 1,2월 sumWinterChungcunDay += 1; sumWinterChungcunFlow += Number(item2.inflAmnt); //item2.inflAmnt && item2.inflAmnt !== 'x' ? Number(item2.inflAmnt.toString().trim()) : 0; } } totWaterUsage += Number(item2.inflAmnt); - if(item2.msrDt.substring(4,6) ==='06' || item2.msrDt.substring(4,6) ==='07' || item2.msrDt.substring(4,6) ==='08'){ + if (item2.msrDt.substring(4, 6) === '06' || item2.msrDt.substring(4, 6) === '07' || item2.msrDt.substring(4, 6) === '08') { // 하절기 6,7,8월 totSummerDays += 1; totSummerUsage += Number(item2.inflAmnt); //item2.inflAmnt && item2.inflAmnt !== 'x' ? Number(item2.inflAmnt.toString().trim()) : 0; - }else if(item2.msrDt.substring(4,6) ==='01' || item2.msrDt.substring(4,6) ==='02'){ + } else if (item2.msrDt.substring(4, 6) === '01' || item2.msrDt.substring(4, 6) === '02') { // 동절기 1,2월 totWinterDays += 1; totWinterUsage += Number(item2.inflAmnt); //item2.inflAmnt && item2.inflAmnt !== 'x' ? Number(item2.inflAmnt.toString().trim()) : 0; @@ -447,8 +471,8 @@ export const underWaterReducer:(set:(fn: (state: UnderWaterType) => UnderWaterTy }); const count = detailList ? detailList.length : 0; - const avg = count == 0 ? 0 : totWaterUsage / count; // 일평균 유입량 - + const avg = count == 0 ? 0 : totWaterUsage / count; // 연간 일평균 유입량 + let wtrUsgYearTotal = 0; // 연간 물사용량 let summerWtrUsgTot = 0; // 하절기 물사용량 let winterWtrUsgTot = 0; // 동절기 물사용량 @@ -459,52 +483,52 @@ export const underWaterReducer:(set:(fn: (state: UnderWaterType) => UnderWaterTy const days = getDaysInMonth(item3.levyYm); wtrUsgYearTotal += Number(item3.wtrUsg); wtrUsgYearDays += days; - if(summerMonth.includes(item3.levyYm.slice(-2))){ + if (summerMonth.includes(item3.levyYm.slice(-2))) { summerWtrUsgTot += Number(item3.wtrUsg); summerWtrUsgDays += days; - }else if(winterMonth.includes(item3.levyYm.slice(-2))){ + } else if (winterMonth.includes(item3.levyYm.slice(-2))) { winterWtrUsgTot += Number(item3.wtrUsg); winterWtrUsgDays += days; } }); - + if (wtrUsgYearTotal > 0) { // 물사용량이 있는 경우 - const usage = chungcunSumFlow&&chungcunSumFlow>0?chungcunSumFlow / sumChungcunDay:0; // 청천시 일평균 유입량 - const summerUsage = sumSummerChungcunFlow?sumSummerChungcunFlow / sumSummerChungcunDay:0; // 하절기 청천시 일평균 유입량 - const winterUsage = sumWinterChungcunFlow?sumWinterChungcunFlow / sumWinterChungcunDay:0; // 동절기 청천시 일평균 유입량 - const wtrUsg = wtrUsgYearDays>0? wtrUsgYearTotal / wtrUsgYearDays: 0; // 일평균 물사용량 - const summerWtrUsg = summerWtrUsgTot?summerWtrUsgTot / summerWtrUsgDays:0; // 하절기 일평균 물사용량 - const winterWtrUsg = winterWtrUsgTot?winterWtrUsgTot / winterWtrUsgDays:0; // 동절기 일평균 물사용량 - + const usage = chungcunSumFlow && chungcunSumFlow > 0 ? chungcunSumFlow / sumChungcunDay : 0; // 청천시 일평균 유입량 + const summerUsage = sumSummerChungcunFlow ? sumSummerChungcunFlow / sumSummerChungcunDay : 0; // 하절기 청천시 일평균 유입량 + const winterUsage = sumWinterChungcunFlow ? sumWinterChungcunFlow / sumWinterChungcunDay : 0; // 동절기 청천시 일평균 유입량 + const wtrUsg = wtrUsgYearDays > 0 ? wtrUsgYearTotal / wtrUsgYearDays : 0; // 일평균 물사용량 + const summerWtrUsg = summerWtrUsgTot ? summerWtrUsgTot / summerWtrUsgDays : 0; // 하절기 일평균 물사용량 + const winterWtrUsg = winterWtrUsgTot ? winterWtrUsgTot / winterWtrUsgDays : 0; // 동절기 일평균 물사용량 + item.inflAmnt = roundNumber(avg, 0); - item.underWaterInputYear = Math.round((usage-(wtrUsg*OSU_PERCENT)) * 1) / 1; // 연간지하수유입량(m³/일) = 청천시 실제유입량 - (물사용량 × 오수전환율) - item.underWaterInputSummer = Math.round((summerUsage-(summerWtrUsg*OSU_PERCENT)) * 1) / 1; // 하절기지하수유입량(m³/일) - item.underWaterInputWinter = Math.round((winterUsage-(winterWtrUsg*OSU_PERCENT)) * 1) / 1; // 동절기지하수유입량(m³/일) - item.properYn = Math.round( (Number(usage-(wtrUsg*OSU_PERCENT)) / Number(item.inflAmnt)) * 100 * 1 ) / 1 > 20?'초과유입':'적정'; // 적정여부 + item.underWaterInputYear = Math.round((usage - (wtrUsg * OSU_PERCENT)) * 1) / 1; // 연간지하수유입량(m³/일) = 청천시 실제유입량 - (물사용량 × 오수전환율) + item.underWaterInputSummer = Math.round((summerUsage - (summerWtrUsg * OSU_PERCENT)) * 1) / 1; // 하절기지하수유입량(m³/일) + item.underWaterInputWinter = Math.round((winterUsage - (winterWtrUsg * OSU_PERCENT)) * 1) / 1; // 동절기지하수유입량(m³/일) + item.properYn = Math.round((Number(usage - (wtrUsg * OSU_PERCENT)) / Number(item.inflAmnt)) * 100 * 1) / 1 > 20 ? '초과유입' : '적정'; // 적정여부 } else { // 물사용량이 없는 경우, 일평균 유입량으로 물사용량 추정(물사용량 = 유입량*0.65) - const usage = chungcunSumFlow&&chungcunSumFlow>0?chungcunSumFlow / sumChungcunDay:0; // 청천시 일평균 유입량 - const summerUsage = sumSummerChungcunFlow?sumSummerChungcunFlow / sumSummerChungcunDay:0; // 하절기 청천시 일평균 유입량 - const winterUsage = sumWinterChungcunFlow?sumWinterChungcunFlow / sumWinterChungcunDay:0; // 동절기 청천시 일평균 유입량 + const usage = chungcunSumFlow && chungcunSumFlow > 0 ? chungcunSumFlow / sumChungcunDay : 0; // 청천시 일평균 유입량 + const summerUsage = sumSummerChungcunFlow ? sumSummerChungcunFlow / sumSummerChungcunDay : 0; // 하절기 청천시 일평균 유입량 + const winterUsage = sumWinterChungcunFlow ? sumWinterChungcunFlow / sumWinterChungcunDay : 0; // 동절기 청천시 일평균 유입량 const wtrUsg = avg * 0.65; // 일평균 물사용량 - const summerWtrUsg = totSummerDays>0? (totSummerUsage / totSummerDays)*0.65:0; // 하절기 일평균 물사용량 - const winterWtrUsg = totWinterDays>0? (totWinterUsage / totWinterDays)*0.65:0; // 동절기 일평균 물사용량 - + const summerWtrUsg = totSummerDays > 0 ? (totSummerUsage / totSummerDays) * 0.65 : 0; // 하절기 일평균 물사용량 + const winterWtrUsg = totWinterDays > 0 ? (totWinterUsage / totWinterDays) * 0.65 : 0; // 동절기 일평균 물사용량 + item.inflAmnt = roundNumber(avg, 0); //item.underWaterInputYear = Math.round((usage-(usage*0.65*OSU_PERCENT)) * 1) / 1; // 연간지하수유입량(m³/일) - item.underWaterInputYear = Math.round((usage-(wtrUsg*OSU_PERCENT)) * 1) / 1; // 연간지하수유입량(m³/일) = 청천시 실제유입량 - (물사용량 × 오수전환율) - item.underWaterInputSummer = Math.round((summerUsage-(summerWtrUsg*OSU_PERCENT)) * 1) / 1; // 하절기지하수유입량(m³/일) = 청천시 실제유입량 - (물사용량 × 오수전환율) - item.underWaterInputWinter = Math.round((winterUsage-(winterWtrUsg*OSU_PERCENT)) * 1) / 1; // 동절기지하수유입량(m³/일) = 청천시 실제유입량 - (물사용량 × 오수전환율) - item.properYn = Math.round( (Number(usage-(wtrUsg*OSU_PERCENT)) / Number(item.inflAmnt)) * 100 * 1 ) / 1 > 20?'초과유입':'적정'; // 적정여부 - // 반올림 처리 - // ["fctCpc", "inflAmnt", "underWaterInputYear", "underWaterInputSummer", "underWaterInputWinter"].forEach( - // (key) => (item[key] = roundNumber(item[key], 0)) - // ); + item.underWaterInputYear = Math.round((usage - (wtrUsg * OSU_PERCENT)) * 1) / 1; // 연간지하수유입량(m³/일) = 청천시 실제유입량 - (물사용량 × 오수전환율) + item.underWaterInputSummer = Math.round((summerUsage - (summerWtrUsg * OSU_PERCENT)) * 1) / 1; // 하절기지하수유입량(m³/일) = 청천시 실제유입량 - (물사용량 × 오수전환율) + item.underWaterInputWinter = Math.round((winterUsage - (winterWtrUsg * OSU_PERCENT)) * 1) / 1; // 동절기지하수유입량(m³/일) = 청천시 실제유입량 - (물사용량 × 오수전환율) + item.properYn = Math.round((Number(usage - (wtrUsg * OSU_PERCENT)) / Number(item.inflAmnt)) * 100 * 1) / 1 > 20 ? '초과유입' : '적정'; // 적정여부 + // 반올림 처리 + // ["fctCpc", "inflAmnt", "underWaterInputYear", "underWaterInputSummer", "underWaterInputWinter"].forEach( + // (key) => (item[key] = roundNumber(item[key], 0)) + // ); } } - if(pageSize>= 5000){ + if (pageSize >= 5000) { const excelDownList: ExcelType[] = []; - underWaterPropsList.forEach((item : any) => { + underWaterPropsList.forEach((item: any) => { dataFormSetting(item) item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, '')); excelDownList.push({ @@ -530,10 +554,10 @@ export const underWaterReducer:(set:(fn: (state: UnderWaterType) => UnderWaterTy UnderWater: { ...state.UnderWater, excelList: excelDownList, - isLoading : false, + isLoading: false, }, })); - }else{ + } else { set((state: UnderWaterType) => ({ ...state, UnderWater: { @@ -541,21 +565,21 @@ export const underWaterReducer:(set:(fn: (state: UnderWaterType) => UnderWaterTy underWaterList: underWaterPropsList, underWaterCount: facilityInitData.count, excelList: [], - isLoading : false, + isLoading: false, }, })); } }, - - unserWaterModal:async (bscFctCd, searchYear, startYm, endYm)=>{ + + underWaterModal: async (bscFctCd, searchYear, startYm, endYm) => { const fcltCdList = [bscFctCd]; - const wtrUsgRes : any = await fetchWtrUsgYmList(searchYear, fcltCdList); + const wtrUsgRes: any = await fetchWtrUsgYmList(searchYear, fcltCdList); const res = await getFetch('fo', '/fclt/flow/' + bscFctCd + '/' + searchYear); const monthData: SewageOccurDetailProps[] = []; let tot = 0, cnt = 0; let sunnyTot = 0, sunnyCnt = 0; - - if(startYm) { + + if (startYm) { res.data = res.data.filter((item: FlowDataItem) => { const month = item.msrDt?.substring(0, 6); return month >= startYm; @@ -567,7 +591,7 @@ export const underWaterReducer:(set:(fn: (state: UnderWaterType) => UnderWaterTy }); } } - if(endYm) { + if (endYm) { res.data = res.data.filter((item: FlowDataItem) => { const month = item.msrDt?.substring(0, 6); return month <= endYm; @@ -582,36 +606,40 @@ export const underWaterReducer:(set:(fn: (state: UnderWaterType) => UnderWaterTy let rainEffectDay = 0; res.data?.forEach(item2 => { if (item2.infoRnDay == null || item2.infoRnDay.trim() == 'x') item2.infoRnDay = 0; - if(Number(item2.infoRnDay) >= 3) { + if (Number(item2.infoRnDay) >= 3) { rainEffectDay = 2; - }else{ + } else { rainEffectDay--; } - + const month = item2.msrDt?.substring(4, 6); const monthIdx = month ? Number(month) - 1 : -1; - if (monthIdx >= 0 && (monthIdx>=Number(startYm.substring(4,6))-1 && monthIdx <= Number(endYm.substring(4, 6))-1)) { + if (monthIdx >= 0 && (monthIdx >= Number(startYm.substring(4, 6)) - 1 && monthIdx <= Number(endYm.substring(4, 6)) - 1)) { if (typeof monthData[monthIdx] === 'object') { monthData[monthIdx].sum += Number(item2.inflAmnt); // 유입량 합 monthData[monthIdx].cnt++; } else { - monthData[monthIdx] = {cnt:1, sum:Number(item2.inflAmnt), yr:item2.msrDt.substring(0,4) - , usage:0, sunnyCnt:0, sunnyInflAmnt:0, sunnyAvgInflAmnt: 0, cntWtrUsg:0, sumWtrUsg:0, avgWtrUsg:0}; + monthData[monthIdx] = { + cnt: 1, sum: Number(item2.inflAmnt), yr: item2.msrDt.substring(0, 4) + , usage: 0, sunnyCnt: 0, sunnyInflAmnt: 0, sunnyAvgInflAmnt: 0, cntWtrUsg: 0, sumWtrUsg: 0, avgWtrUsg: 0 + }; } - } - if(month >= startYm.substring(4, 6) && month <= endYm.substring(4, 6)){ + } + if (month >= startYm.substring(4, 6) && month <= endYm.substring(4, 6)) { cnt++; tot += item2.inflAmnt && item2.inflAmnt !== 'x' ? Number(item2.inflAmnt.toString().trim()) : 0; } - if(rainEffectDay<0) { // 청천시 + if (rainEffectDay < 0) { // 청천시 if (typeof monthData[monthIdx] === 'object') { monthData[monthIdx].sunnyInflAmnt += Number(item2.inflAmnt); // 청천 유입량 합 monthData[monthIdx].sunnyCnt++; - }else { - monthData[monthIdx] = {cnt:1, sum:Number(item2.inflAmnt), yr:item2.msrDt.substring(0,4) - , usage:0, sunnyCnt:0, sunnyInflAmnt:0, sunnyAvgInflAmnt: 0, cntWtrUsg:0, sumWtrUsg:0, avgWtrUsg:0}; + } else { + monthData[monthIdx] = { + cnt: 1, sum: Number(item2.inflAmnt), yr: item2.msrDt.substring(0, 4) + , usage: 0, sunnyCnt: 0, sunnyInflAmnt: 0, sunnyAvgInflAmnt: 0, cntWtrUsg: 0, sumWtrUsg: 0, avgWtrUsg: 0 + }; } - if (month >= startYm.substring(4, 6) && month <= endYm.substring(4, 6)){ + if (month >= startYm.substring(4, 6) && month <= endYm.substring(4, 6)) { sunnyCnt++; sunnyTot += item2.inflAmnt && item2.inflAmnt !== 'x' ? Number(item2.inflAmnt.toString().trim()) : 0; } @@ -630,77 +658,78 @@ export const underWaterReducer:(set:(fn: (state: UnderWaterType) => UnderWaterTy if (typeof monthData[monthIdx] === 'object') { monthData[monthIdx].sumWtrUsg += Number(item.wtrUsg); monthData[monthIdx].cntWtrUsg += days; - } + } } totWtrUsg += Number(item.wtrUsg); cntWtrUsg += days; }); } - - const tableData:UnderWaterInputModalProps[] = [] - - let maxWaterUsage = 0; - let minWaterUsage = 9999999999; - let avgWaterUsage = 0; - let maxSunnyInflAmnt = 0; - let minSunnyInflAmnt = 9999999999; - let avgSunnyInflAmnt = 0; + + const tableData: UnderWaterInputModalProps[] = [] + + let maxWaterUsage = 0, minWaterUsage = 9999999999, avgWaterUsage = 0; // 물사용량 + let maxSunnyInflAmnt = 0, minSunnyInflAmnt = 9999999999, avgSunnyInflAmnt = 0; // 청천 유입량 + let maxUnderWaterInput = 0, minUnderWaterInput = 9999999999; // 지하수 유입량 monthData.forEach((item, index) => { - item.usage = item.sum/item.cnt; // 일평균 유입량 - item.avgWtrUsg = item.sum/item.cnt * 0.65; // 일평균 물사용량 - if (item.sumWtrUsg > 0 && item.cntWtrUsg > 0) item.avgWtrUsg = item.sumWtrUsg / item.cntWtrUsg; // 일평균 물사용량 + item.usage = item.sum / item.cnt; // 일평균 유입량 + item.avgWtrUsg = item.sum / item.cnt * 0.65; // 일평균 물사용량 : 수용가 물사용량이 없는 경우 + // 일평균 물사용량 : 수용가 물사용량이 있는 경우 + if (item.sumWtrUsg > 0 && item.cntWtrUsg > 0) item.avgWtrUsg = item.sumWtrUsg / item.cntWtrUsg; item.sunnyAvgInflAmnt = item.sunnyInflAmnt / item.sunnyCnt; // 청천 일평균 유입량 + const underWtrInput = Math.round((item.sunnyAvgInflAmnt - (item.avgWtrUsg * OSU_PERCENT)) * 1) / 1; // 지하수 유입량 평균 tableData.push({ - msrDt: item.yr+'-'+(index+1>=10?String(index+1):'0'+(index+1)), - realSewageInput:Math.round(item.sunnyAvgInflAmnt*1) /1, // 청천 실제 하수유입량 - sewageOccur:Math.round(item.avgWtrUsg*OSU_PERCENT * 1) / 1,// 오수발생량 - underWaterInput:Math.round((item.sunnyAvgInflAmnt-(item.avgWtrUsg*OSU_PERCENT)) * 1) / 1, // 지하수 유입량 : 하수처리장 청천시 실제유입량 - (물사용량 × 오수전환율) + msrDt: item.yr + '-' + (index + 1 >= 10 ? String(index + 1) : '0' + (index + 1)), + realSewageInput: Math.round(item.sunnyAvgInflAmnt * 1) / 1, // 청천 실제 하수유입량 + sewageOccur: Math.round(item.avgWtrUsg * OSU_PERCENT * 1) / 1,// 오수발생량 + underWaterInput: underWtrInput, // 지하수 유입량 : 하수처리장 청천시 실제유입량 - (물사용량 × 오수전환율) }) - if (item.avgWtrUsg > maxWaterUsage) maxWaterUsage = item.avgWtrUsg; + if (item.avgWtrUsg > maxWaterUsage) maxWaterUsage = item.avgWtrUsg; if (item.sunnyAvgInflAmnt > maxSunnyInflAmnt) maxSunnyInflAmnt = item.sunnyAvgInflAmnt; if (item.avgWtrUsg < minWaterUsage) minWaterUsage = item.avgWtrUsg; if (item.sunnyAvgInflAmnt < minSunnyInflAmnt) minSunnyInflAmnt = item.sunnyAvgInflAmnt; + if (underWtrInput > maxUnderWaterInput) maxUnderWaterInput = underWtrInput; + if (underWtrInput < minUnderWaterInput) minUnderWaterInput = underWtrInput; }); - if(monthData.length == 0){ - maxWaterUsage = 0; minWaterUsage = 0; avgWaterUsage = 0; + if (monthData.length == 0) { + maxWaterUsage = 0; minWaterUsage = 0; avgWaterUsage = 0; maxSunnyInflAmnt = 0; minSunnyInflAmnt = 0; avgSunnyInflAmnt = 0; } - - avgWaterUsage = cnt>0? tot/cnt * 0.65:0; // 일평균 물사용량 - if (totWtrUsg > 0 && cntWtrUsg > 0) avgWaterUsage = totWtrUsg / cntWtrUsg; // 일평균 물사용량 - avgSunnyInflAmnt = (sunnyCnt>0)?sunnyTot/sunnyCnt:0; // 청천 일평균 유입량 - const newData= [ - { - msrDt: '평균', - realSewageInput:Math.round(avgSunnyInflAmnt*1) /1, // 청천시 실제 하수유입량 - sewageOccur:Math.round(avgWaterUsage*OSU_PERCENT * 1) / 1, // 오수발생량 - underWaterInput: Math.round((avgSunnyInflAmnt-(avgWaterUsage*OSU_PERCENT)) * 1) / 1 // 지하수 유입량 : 하수처리장 청천시 실제유입량 - (물사용량 × 오수전환율) - //underWaterInput:Math.round((avgWaterUsage-(avgWaterUsage*0.65*0.8)) * 100) / 100, - }, - { - msrDt: '최대', - realSewageInput: Math.round(maxSunnyInflAmnt*1) /1, // 청천시 실제 하수유입량 - sewageOccur: Math.round(maxWaterUsage*OSU_PERCENT * 1) / 1, // 오수발생량 - underWaterInput: Math.round((maxSunnyInflAmnt-(maxWaterUsage*OSU_PERCENT)) * 1) / 1, // 지하수 유입량 : 하수처리장 청천시 실제유입량 - (물사용량 × 오수전환율) - }, - { - msrDt: '최소', - realSewageInput: Math.round(minSunnyInflAmnt*1) /1, // 청천시 실제 하수유입량 - sewageOccur: Math.round(minWaterUsage*OSU_PERCENT * 1) / 1, // 오수발생량 - underWaterInput: Math.round((minSunnyInflAmnt-(minWaterUsage*OSU_PERCENT)) * 1) / 1, // 지하수 유입량 : 하수처리장 청천시 실제유입량 - (물사용량 × 오수전환율) - }, - ...tableData + avgWaterUsage = cnt > 0 ? tot / cnt * 0.65 : 0; // 일평균 물사용량 + if (totWtrUsg > 0 && cntWtrUsg > 0) avgWaterUsage = totWtrUsg / cntWtrUsg; // 일평균 물사용량 + avgSunnyInflAmnt = (sunnyCnt > 0) ? sunnyTot / sunnyCnt : 0; // 청천 일평균 유입량 + + const newData = [ + { + msrDt: '평균', + realSewageInput: Math.round(avgSunnyInflAmnt * 1) / 1, // 청천시 실제 하수유입량 + sewageOccur: Math.round(avgWaterUsage * OSU_PERCENT * 1) / 1, // 오수발생량 + underWaterInput: Math.round((avgSunnyInflAmnt - (avgWaterUsage * OSU_PERCENT)) * 1) / 1 // 지하수 유입량 : 하수처리장 청천시 실제유입량 - (물사용량 × 오수전환율) + //underWaterInput:Math.round((avgWaterUsage-(avgWaterUsage*0.65*0.8)) * 100) / 100, + }, + { + msrDt: '최대', + realSewageInput: Math.round(maxSunnyInflAmnt * 1) / 1, // 청천시 실제 하수유입량 + sewageOccur: Math.round(maxWaterUsage * OSU_PERCENT * 1) / 1, // 오수발생량 + underWaterInput: maxUnderWaterInput, // 지하수 유입량 : 하수처리장 청천시 실제유입량 - (물사용량 × 오수전환율) + }, + { + msrDt: '최소', + realSewageInput: Math.round(minSunnyInflAmnt * 1) / 1, // 청천시 실제 하수유입량 + sewageOccur: Math.round(minWaterUsage * OSU_PERCENT * 1) / 1, // 오수발생량 + underWaterInput: minUnderWaterInput, // 지하수 유입량 : 하수처리장 청천시 실제유입량 - (물사용량 × 오수전환율) + }, + ...tableData ] set((state: UnderWaterType) => ({ ...state, UnderWaterDetail: { ...state.UnderWaterDetail, - gridListUnderWaterDetail:newData + gridListUnderWaterDetail: newData } })) } } -} \ No newline at end of file +} diff --git a/apps/fo/next.config.ts b/apps/fo/next.config.ts index 709fd3cf6..6a892461e 100644 --- a/apps/fo/next.config.ts +++ b/apps/fo/next.config.ts @@ -1,7 +1,7 @@ import type { NextConfig } from "next"; -const subDomain = - process.env.NEXT_PUBLIC_PROXY_HOST || "http://localhost:3000"; +const domain = process.env.NEXT_PUBLIC_PROXY_HOST || "http://localhost:3000"; +const subDomain = process.env.NEXT_PUBLIC_SUB_DOMAIN || "http://localhost:13000"; const nextConfig: NextConfig = { output: "standalone", @@ -18,7 +18,7 @@ const nextConfig: NextConfig = { { type: "header", key: "referer", - value: "(.*)/pst(.*)", + value: "(.*)/cms(.*)", }, ], }, @@ -26,19 +26,23 @@ const nextConfig: NextConfig = { afterFiles: [ { source: "/sewerage-service/:path*", - destination: `${subDomain}/sewerage-service/:path*`, + destination: `${domain}/sewerage-service/:path*`, }, { source: "/reuse-service/:path*", - destination: `${subDomain}/reuse-service/:path*`, + destination: `${domain}/reuse-service/:path*`, }, { source: "/operation-service/:path*", - destination: `${subDomain}/operation-service/:path*`, + destination: `${domain}/operation-service/:path*`, }, { source: "/ngii-server/:path*", - destination: `${subDomain}/ngii-server/:path*`, + destination: `${domain}/ngii-server/:path*`, + }, + { + source: "/cms/:path*", + destination: `${subDomain}/cms/:path*`, }, ], fallback: [], diff --git a/apps/fo/src/entities/auth/lib/auth-wrapper.tsx b/apps/fo/src/entities/auth/lib/auth-wrapper.tsx index 0c2d04830..864b54ce0 100644 --- a/apps/fo/src/entities/auth/lib/auth-wrapper.tsx +++ b/apps/fo/src/entities/auth/lib/auth-wrapper.tsx @@ -1,7 +1,7 @@ "use client"; import { useEffect } from "react"; -import { useRouter, usePathname } from "next/navigation"; +import { useRouter } from "next/navigation"; import { client } from "@/src/shared/api"; import type { IUserInfo } from "@/src/shared/store"; import { useUserStore, getCookie } from "@/src/shared/lib"; @@ -12,7 +12,7 @@ const LOGIN_PAGE_URL = process.env.NEXT_PUBLIC_CM_LOGIN_PAGE_URL || "http://loca const AuthWrapper = ({ children }: { children: React.ReactNode }) => { const router = useRouter(); - const pathname = usePathname(); + // const pathname = usePathname(); const { userInfo, setUserInfo } = useUserStore((state) => state); async function authCheck() { @@ -53,7 +53,7 @@ const AuthWrapper = ({ children }: { children: React.ReactNode }) => { } } })(); - }, [pathname]); + }, []); return <>{children}</>; }; diff --git a/apps/fo/src/shared/providers/prompt-provider.tsx b/apps/fo/src/shared/providers/prompt-provider.tsx index 10338c338..7724d0326 100644 --- a/apps/fo/src/shared/providers/prompt-provider.tsx +++ b/apps/fo/src/shared/providers/prompt-provider.tsx @@ -1,3 +1,4 @@ +"use client"; import { createContext, useState } from "react"; interface PromptContext { diff --git a/apps/sn/.env.mindone b/apps/sn/.env.mindone index d03f5cae5..cea5518db 100644 --- a/apps/sn/.env.mindone +++ b/apps/sn/.env.mindone @@ -5,7 +5,7 @@ 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:11302 -NEXT_PUBLIC_SUB_DOMAIN = http://con.mind-one.co.kr:11300 +NEXT_PUBLIC_SUB_DOMAIN = http://con.mind-one.co.kr:13000 NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000 NEXT_PUBLIC_GEOSERVER_URL = https://hasudo.geo.rsonesoft.com/geoserver NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver diff --git a/apps/sn/.env.production b/apps/sn/.env.production index b166d7390..c68db7859 100644 --- a/apps/sn/.env.production +++ b/apps/sn/.env.production @@ -11,7 +11,7 @@ NEXT_PUBLIC_REFRESH_TOKEN = refresh-token NEXT_PUBLIC_SERVER_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000 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_NGII_URL = http://172.29.143.87 +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_SEWERAGE_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000 NEXT_PUBLIC_DC_URL = http://apigateway.iipbs-was.svc.cluster.local:8000 diff --git a/apps/sn/app/(map-layout)/map/components/common/table.ts b/apps/sn/app/(map-layout)/map/components/common/table.ts index 081e13d1f..2aa3d0ac5 100644 --- a/apps/sn/app/(map-layout)/map/components/common/table.ts +++ b/apps/sn/app/(map-layout)/map/components/common/table.ts @@ -27,7 +27,8 @@ export const MsSwagePplCol = [ // { id: 'ftre_smbol_cd', title: '지형지물부호코드' }, // { id: 'lyrTypeNm', title: '레이어타입명' }, ] -export const MsSwagePplCol2 = [ +// 하수관로 searchModal 기본정보tab +export const MsSwagePplCol2 = [ { id: 'rowNumber', title: '일련\n번호' }, { id: 'fcltMngNo', title: '시설\n관리번호' }, { id: 'instMngNo', title: '기관\n관리번호' }, @@ -44,13 +45,13 @@ export const MsSwagePplCol2 = [ { id: 'tmptMhlMngNo', title: '시점맨홀\n관리번호' }, { id: 'scendDpth', title: '종점깊이\n(m)' }, { id: 'scendMhlMngNo', title: '종점맨홀\n관리번호' }, - // { id: 'vrtcLen', title: '세로길이\n(m)' }, - // { id: 'wdthLen', title: '가로길이\n(m)' }, - // { id: 'fcltyShpCd', title: '시설물형태코드' }, + { id: 'vrtcLen', title: '세로길이\n(m)' }, + { id: 'wdthLen', title: '가로길이\n(m)' }, + { id: 'fcltyShpCd', title: '시설물형태코드' }, // { id: 'fid', title: 'FID' }, // { id: 'ftreSmbolCd', title: '지형지물부호코드' }, // { id: 'mngInstCd', title: '관리기관코드' }, - // { id: 'mqltCd', title: '재질코드' }, + { id: 'mqltCd', title: '재질코드' }, // { id: 'pbadmsEmdCd', title: '행정읍면동코드' }, // { id: 'usgCd', title: '용도코드' }, // { id: 'lyrTypeNm', title: '레이어타입명' }, @@ -69,6 +70,10 @@ export const MsSwagePplExcelCol = { 'tmptMhlMngNo': '시점맨홀 관리번호', 'scendDpth': '종점 깊이(m)', 'scendMhlMngNo': '종점맨홀 관리번호', + 'vrtcLen': '세로길이(m)', + 'wdthLen': '가로길이(m)', + 'fcltyShpCd': '시설물형태코드', + 'mqltCd': '재질코드', }; // 하수관로 용도 코드표 export const MS_SWAGE_PPL_USE_CODES = [ @@ -275,7 +280,80 @@ export const MS_SWAGE_PPL_CODES = [ { val: "MOP977", text: "전주용입상관" }, { val: "MOP978", text: "MD관" }, ]; +// 하수관로 'fcltyShpCd' '시설물형태코드' +export const MS_SWAGE_PPL_SHAPE = [ + { val: "FOR000 ", text: "미분류" }, + { val: "FOR001 ", text: "원형" }, + { val: "FOR002 ", text: "원추형" }, + { val: "FOR003 ", text: "원통형" }, + { val: "FOR004 ", text: "구형" }, + { val: "FOR005 ", text: "타원형" }, + { val: "FOR006 ", text: "난원" }, + { val: "FOR007 ", text: "반원형" }, + { val: "FOR010 ", text: "피라미드형" }, + { val: "FOR011 ", text: "사면체형" }, + { val: "FOR012 ", text: "기둥,축" }, + { val: "FOR013 ", text: "돛대,활대처럼 둥글고 긴 원재" }, + { val: "FOR014 ", text: "아치형" }, + { val: "FOR015 ", text: "다중아치형" }, + { val: "FOR016 ", text: "돔형" }, + { val: "FOR017 ", text: "고형체형" }, + + { val: "FOR020 ", text: "사각형" }, + { val: "FOR021 ", text: "평방형" }, + { val: "FOR022 ", text: "다이아몬드형" }, + { val: "FOR023 ", text: "사다리꼴" }, + { val: "FOR024 ", text: "각형" }, + { val: "FOR025 ", text: "마제형관" }, + { val: "FOR026 ", text: "태형" }, + + { val: "FOR030 ", text: "A형" }, + { val: "FOR031 ", text: "H형" }, + { val: "FOR032 ", text: "I형" }, + { val: "FOR033 ", text: "Y형" }, + { val: "FOR034 ", text: "T형" }, + + { val: "FOR050 ", text: "불규칙한 형태" }, + + { val: "FOR900 ", text: "계란형" }, + { val: "FOR901 ", text: "구(사각)형" }, + { val: "FOR902 ", text: "높이조절철개형" }, + { val: "FOR903 ", text: "박스형(RCB)" }, + { val: "FOR904 ", text: "반월개거" }, + { val: "FOR905 ", text: "반월개거(측구)" }, + { val: "FOR906 ", text: "벽돌조" }, + { val: "FOR907 ", text: "사각철판" }, + { val: "FOR908 ", text: "석축개거" }, + { val: "FOR909 ", text: "석축개거(측구)" }, + { val: "FOR910 ", text: "승수로" }, + { val: "FOR911 ", text: "옹벽개거" }, + { val: "FOR912 ", text: "옹벽개거(측구)" }, + { val: "FOR913 ", text: "이단" }, + { val: "FOR914 ", text: "이중침전형" }, + { val: "FOR915 ", text: "자연개거" }, + { val: "FOR916 ", text: "자연개거(측구)" }, + { val: "FOR917 ", text: "조립식" }, + { val: "FOR918 ", text: "지상관" }, + { val: "FOR919 ", text: "지상단구" }, + { val: "FOR920 ", text: "지상쌍구" }, + { val: "FOR921 ", text: "지하단구" }, + { val: "FOR922 ", text: "지하쌍구" }, + { val: "FOR923 ", text: "직사각형" }, + { val: "FOR924 ", text: "타설식" }, + { val: "FOR925 ", text: "토류형" }, + { val: "FOR926 ", text: "흄관원형" }, + { val: "FOR927 ", text: "L형측구" }, + { val: "FOR928 ", text: "LO형측구" }, + { val: "FOR929 ", text: "LU형측구" }, + { val: "FOR930 ", text: "PVC이중벽관" }, + { val: "FOR931 ", text: "RCB 맨홀" }, + { val: "FOR932 ", text: "U형" }, + { val: "FOR933 ", text: "U형측구(미복개)" }, + { val: "FOR934 ", text: "U형측구(복개)" }, + + { val: "FOR999 ", text: "기타" } +]; // 하수처리시설 export const SwtrmtFcltCol = [ { id: 'swtrmt_fclt_nm', title: '하수처리시설명' }, diff --git a/apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx b/apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx index 34cdb674b..d179652f3 100644 --- a/apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx +++ b/apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx @@ -125,10 +125,9 @@ const SearchModal: React.FC = () => { {/* 📋 결과 리스트 */} {/* 기본정보 탭 */} {activeTab === 'basic' && ( - <div className="h-[79vh] w-full overflow-x-hidden overflow-y-auto rounded-lg border mt-4"> - <div className="flex-1 px-2 pb-2"> - <LoadingOverlay isLoading={isLoading} /> - <UiTable + <div className="h-[79dvh] w-full rounded-lg border mt-4 overflow-x-scroll"> + <LoadingOverlay isLoading={isLoading} /> + <UiTable className='w-[120%]' headName={(() => { const label = BASIC_TYPES.find((type) => type.val === basicType)?.text return (<>{label} 조회 결과 ({tableCount.toLocaleString('ko-KR')})건</>); @@ -150,66 +149,65 @@ const SearchModal: React.FC = () => { </div> } /> - </div> </div> )} {/* 유지관리 정보 탭 */} {activeTab === 'operation' && ( <> - <div className="h-[39vh] w-full overflow-hidden rounded-lg border mt-4"> - <div className="flex-1 px-2 pb-2"> - <LoadingOverlay isLoading={isLoading} /> - <UiTable - headName={ - (() => { - const label = WORK_TYPES.find((type) => type.val === workType)?.text - return (<>{label} 사업 현황 ({tableCount.toLocaleString('ko-KR')})건</>); - })() - } - tableData={tableData} - headlist={dynamicHeadlist} - pageSize={OPERATION_TABLE_SIZE} - total={tableCount} - cellClick={handleOperationCellClick} - onPageChange={setCurPage} - resetPageNo={isResetPage} - resetSelection={isResetSelection} - curPage={curPage} - headButton={ - <Button className="px-4 py-2 !mr-[1.2rem] text-white rounded-sm text-sm font-medium green" size="sm" onClick={operationExcelDownload}>파일 다운로드</Button> - } - /> - </div> + <div className="h-[39dvh] w-full overflow-x-hidden overflow-y-auto rounded-lg border mt-4"> + {/* <div className="flex-1 px-2 pb-2"> */} + <LoadingOverlay isLoading={isLoading} /> + <UiTable + headName={ + (() => { + const label = WORK_TYPES.find((type) => type.val === workType)?.text + return (<>{label} 사업 현황 ({tableCount.toLocaleString('ko-KR')})건</>); + })() + } + tableData={tableData} + headlist={dynamicHeadlist} + pageSize={OPERATION_TABLE_SIZE} + total={tableCount} + cellClick={handleOperationCellClick} + onPageChange={setCurPage} + resetPageNo={isResetPage} + resetSelection={isResetSelection} + curPage={curPage} + headButton={ + <Button className="px-4 py-2 !mr-[1.2rem] text-white rounded-sm text-sm font-medium green" size="sm" onClick={operationExcelDownload}>파일 다운로드</Button> + } + /> + {/* </div> */} </div> - <div className="h-[39vh] w-full overflow-hidden rounded-lg border"> + <div className="h-[39dvh] w-full overflow-x-hidden overflow-y-auto rounded-lg border"> {/* 테이블2 영역 */} - <div className="flex-1 px-2 pb-2"> - <LoadingOverlay isLoading={isDetailLoading} /> - <UiTable - headName={ - (() => { - const label = WORK_TYPES.find((type) => type.val === workType)?.text - return (<>{label} 결과표 ({detailTableCount.toLocaleString('ko-KR')})건</>); - })() - } - tableData={detailTableData} - headlist={dynamicHeadlist2} - pageSize={OPERATION_TABLE_SIZE} - total={detailTableCount} - cellClick={handleOperationResultCellClick} - onPageChange={setDetailCurPage} - resetPageNo={isResetDetailPage} - resetSelection={isResetDetailSelection} - curPage={detailCurPage} - headButton={ - <div className='flex'> - <Button className="px-4 py-2 !mr-[1.2rem] text-white rounded-sm text-lg font-medium primary" size="sm" onClick={viewMultipleFeature}>지도에서 보기</Button> - <Button className="px-4 py-2 !mr-[1.2rem] text-white rounded-sm text-lg font-medium green" size="sm" onClick={operationResultExcelDownload}>파일 다운로드</Button> - </div> - } - /> - </div> + {/* <div className="flex-1 px-2 pb-2"> */} + <LoadingOverlay isLoading={isDetailLoading} /> + <UiTable + headName={ + (() => { + const label = WORK_TYPES.find((type) => type.val === workType)?.text + return (<>{label} 결과표 ({detailTableCount.toLocaleString('ko-KR')})건</>); + })() + } + tableData={detailTableData} + headlist={dynamicHeadlist2} + pageSize={OPERATION_TABLE_SIZE} + total={detailTableCount} + cellClick={handleOperationResultCellClick} + onPageChange={setDetailCurPage} + resetPageNo={isResetDetailPage} + resetSelection={isResetDetailSelection} + curPage={detailCurPage} + headButton={ + <div className='flex'> + <Button className="px-4 py-2 !mr-[1.2rem] text-white rounded-sm text-lg font-medium primary" size="sm" onClick={viewMultipleFeature}>지도에서 보기</Button> + <Button className="px-4 py-2 !mr-[1.2rem] text-white rounded-sm text-lg font-medium green" size="sm" onClick={operationResultExcelDownload}>파일 다운로드</Button> + </div> + } + /> + {/* </div> */} </div> </> )} @@ -224,8 +222,8 @@ const SearchModal: React.FC = () => { bodyClass={` h-[100dvh] w-[90dvw] - max-w-[1200px] - max-h-[100dvh] + max-w-[1220px] + max-h-[970px] overflow-y-hidden z-[101] `} diff --git a/apps/sn/app/(map-layout)/map/searchModal/useBasicTab.ts b/apps/sn/app/(map-layout)/map/searchModal/useBasicTab.ts index 80d3deb50..9184286ed 100644 --- a/apps/sn/app/(map-layout)/map/searchModal/useBasicTab.ts +++ b/apps/sn/app/(map-layout)/map/searchModal/useBasicTab.ts @@ -14,7 +14,7 @@ */ import { downloadExcelWithKoreanHeaders } from '@common/business_components/ui'; import { - MS_SWAGE_PPL_USE_CODES, MS_SWAGE_MHL_CODES, MS_SWAGE_MHL_USE_CODES, + MS_SWAGE_PPL_USE_CODES, MS_SWAGE_PPL_CODES, MS_SWAGE_PPL_SHAPE, MS_SWAGE_MHL_CODES, MS_SWAGE_MHL_USE_CODES, MsSwagePplExcelCol, SwtrmtFcltExcelCol, SwtrmtPrcsZoneExcelCol, MsSwageMhlExcelCol, ExclnSewagePumpExcelCol, stofcbrExcelCol } from '../components/common/table'; import { getData } from "@/components/lib/api"; @@ -46,6 +46,7 @@ export default function useBasicTab() { } = useSearchModalStore(); const { ctpv, sgg } = useStore(); + const fetchBasicFilteredData = async () => { let cqlFilters: string[] = []; const queryParams = new URLSearchParams({ @@ -79,8 +80,15 @@ export default function useBasicTab() { if (!sggCd && code) sggCd = code.slice(0, 5); } - const matchedMhlCode = MS_SWAGE_MHL_CODES.find(code => code.val === item?.mqltCd?.trim()); - const matchedMhlUsgCode = MS_SWAGE_MHL_USE_CODES.find(code => code.val === item?.usgCd); + const matchedMqltCode = basicType === 'tb_ms_swage_ppl' + ? MS_SWAGE_PPL_CODES.find(code => code.val === item?.mqltCd?.trim()) // 하수관로 재질 + : basicType === 'tb_ms_swage_mhl' + ? MS_SWAGE_MHL_CODES.find(code => code.val === item?.mqltCd?.trim()) // 맨홀 재질 + : undefined; + const matchedMhlUsgCode = MS_SWAGE_MHL_USE_CODES.find(code => code.val === item?.usgCd); // 맨홀 용도 + const matchedPplShpCode = MS_SWAGE_PPL_SHAPE.find(code => code.val === item?.fcltyShpCd); // 하수관로 시설물형태코드 + + const ctpvNm = ctpv.find(code => code.val.trim() === ctpvCd)?.text || item?.ctpvNm; const sggNm = sgg.find(code => code.val.trim() === sggCd)?.text || item?.sggNm; const usgCdObj = MS_SWAGE_PPL_USE_CODES.find(code => code.val === item?.usgCd?.trim()); @@ -90,15 +98,17 @@ export default function useBasicTab() { ...formattedItem, rowNumber: (curPage - 1) * 10 + index + 1, // 1부터 시작하는 일련번호 추가 scsYn: item.scsYn === null || item.scsYn === undefined ? 'N' : item?.scsYn, // scsYn이 null이면 'N'으로 변경 - mqltCd: matchedMhlCode ? matchedMhlCode.text : item?.mqltCd, + mqltCd: matchedMqltCode ? matchedMqltCode.text : item?.mqltCd, usgCd: matchedMhlUsgCode ? matchedMhlUsgCode.text : (usgCdObj ? usgCdObj.text : item?.usgCd), ctpvNm: ctpvNm ?? '-', sggNm: sggNm ?? '-', swtrmt_zone_nm: item.swtrmtZoneNm || item.swtrmt_zone_nm || '-', + fcltyShpCd: matchedPplShpCode? matchedPplShpCode.text : item?.fcltyShpCd, }; }); setTableData(dataWithRowNumber); + // console.log(tableData); setTableCount(res.count || []); }; @@ -150,7 +160,7 @@ export default function useBasicTab() { // 하수관로 cqlFilters = swagePplCqlFilters() headerMapping = MsSwagePplExcelCol; - headerRange = "A1:M1"; + headerRange = "A1:Q1"; break; } case 'tb_il_swage_prcs_fclt': { @@ -196,6 +206,8 @@ export default function useBasicTab() { const mhlUsgCdMap = new Map(MS_SWAGE_MHL_USE_CODES.map(code => [code.val?.trim(), code.text])); const pplUsgCdMap = new Map(MS_SWAGE_PPL_USE_CODES.map(code => [code.val?.trim(), code.text])); + const pplMqltCdMap = new Map(MS_SWAGE_PPL_CODES.map(code => [code.val?.trim(), code.text])); + const pplShpCdMap = new Map(MS_SWAGE_PPL_SHAPE.map(code => [code.val?.trim(), code.text])); // 관로 시설물형태코드 const ctpvMap = new Map(ctpv.map(code => [code.val?.trim(), code.text])); const sggMap = new Map(sgg.map(code => [code.val?.trim(), code.text])); @@ -232,6 +244,10 @@ export default function useBasicTab() { tmptMhlMngNo: formattedItem.tmptMhlMngNo ?? item.tmptMhlMngNo ?? '-', scendDpth: formattedItem.scendDpth ?? item.scendDpth ?? '-', scendMhlMngNo: formattedItem.scendMhlMngNo ?? item.scendMhlMngNo ?? '-', + vrtcLen: formattedItem.vrtcLen ?? item.vrtcLen ?? '-', + wdthLen: formattedItem.wdthLen ?? item.wdthLen ?? '-', + fcltyShpCd: pplShpCdMap.get(item?.fcltyShpCd?.trim()) || item.fcltyShpCd || formattedItem.fcltyShpCd || '-', + mqltCd: pplMqltCdMap.get(item?.mqltCd?.trim()) || item.mqltCd || formattedItem.mqltCd || '-', }; } case 'tb_il_swage_prcs_fclt': { @@ -329,4 +345,4 @@ export default function useBasicTab() { return { fetchBasicFilteredData, handleBasicCellClick, basicExcelDownload }; -}; \ No newline at end of file +}; diff --git a/apps/sn/app/page.tsx b/apps/sn/app/page.tsx index 1babfe451..2a1db71d8 100644 --- a/apps/sn/app/page.tsx +++ b/apps/sn/app/page.tsx @@ -19,15 +19,14 @@ import { LoginApi } from "@/entities/auth"; import { useEffect } from "react"; import { getCookie } from "@/shared/lib"; import { AUTH_USER_ID } from "@/shared/config"; -import { useRouter } from "next/navigation"; export default function Home() { - const router = useRouter(); const checkToken = getCookie(AUTH_USER_ID); useEffect(() => { + const hostname = window.location.hostname; - + if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "con.mind-one.co.kr") { if (!checkToken) { async function doLogin() { @@ -39,7 +38,7 @@ export default function Home() { } } doLogin(); - } else router.push("/map"); - } else router.push("/map"); + } else window.location.href = "/map"; + } else window.location.href = "/map"; }, []); } diff --git a/apps/sn/next.config.ts b/apps/sn/next.config.ts index 66724c419..ac726f759 100644 --- a/apps/sn/next.config.ts +++ b/apps/sn/next.config.ts @@ -6,16 +6,34 @@ const subDomain = const nextConfig: NextConfig = { output: "standalone", experimental: { - proxyTimeout: 600000, // 10분 (밀리초 단위) - }, - typescript: { - ignoreBuildErrors: true, // 이 줄 추가 + proxyTimeout: 600000, }, eslint: { ignoreDuringBuilds: true, }, + typescript: { + ignoreBuildErrors: true, + }, + logging: { + fetches: { + fullUrl: true, + }, + }, async rewrites() { return { + beforeFiles: [ + { + source: "/_next/static/:path*", + destination: `${subDomain}/_next/static/:path*`, + has: [ + { + type: "header", + key: "referer", + value: "(.*)/cms(.*)", + }, + ], + }, + ], afterFiles: [ { source: "/cms/:path*", diff --git a/apps/sn/src/entities/auth/lib/auth-wrapper.tsx b/apps/sn/src/entities/auth/lib/auth-wrapper.tsx index 289a97087..1c27e6fe3 100644 --- a/apps/sn/src/entities/auth/lib/auth-wrapper.tsx +++ b/apps/sn/src/entities/auth/lib/auth-wrapper.tsx @@ -47,7 +47,7 @@ const AuthWrapper = ({ children }: { children: React.ReactNode }) => { // 개발 환경에서는 localhost 도메인 사용 if (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "con.mind-one.co.kr") - router.push("/"); // 개발시 홈으로 돌아가기 + window.location.href = "/"; // 운영 환경에서는 서브도메인을 포함한 최상위 도메인 사용 // router.push는 같은 Next.js 앱 내에서만 작동하므로 다른 앱으로 이동할 때는 window.location.href 사용 diff --git a/apps/sn/src/shared/config/api.ts b/apps/sn/src/shared/config/api.ts index fe2e39682..16d1b5452 100644 --- a/apps/sn/src/shared/config/api.ts +++ b/apps/sn/src/shared/config/api.ts @@ -3,7 +3,7 @@ import { PROXY_HOST } from "./env"; // 브라우저에서는 동일 오리진으로 상대경로 호출 → CORS/localhost 고정 문제 방지 // (Next middleware가 /server/* 요청을 백엔드로 rewrite) // 서버 환경(SSR 등)에서는 절대 URL이 필요할 수 있어 기존 PROXY_HOST 기반 유지 -export const BASE_URL = typeof window === "undefined" ? `${PROXY_HOST}/server` : "/server"; +export const BASE_URL = `${PROXY_HOST}/server`; export const ENDPOINT = { ATTACH_SERVICE: { diff --git a/apps/wr/.env.local b/apps/wr/.env.local index f418f679e..db59d33c4 100644 --- a/apps/wr/.env.local +++ b/apps/wr/.env.local @@ -5,6 +5,7 @@ NEXT_PUBLIC_AUTH_USER_ID = token-id NEXT_PUBLIC_ACCESS_TOKEN = access-token NEXT_PUBLIC_REFRESH_TOKEN = refresh-token NEXT_PUBLIC_PROXY_HOST = http://localhost:3001 +NEXT_PUBLIC_SUB_DOMAIN = http://localhost:13000 NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000 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 diff --git a/apps/wr/.env.mindone b/apps/wr/.env.mindone index 4a928a910..8749fe39f 100644 --- a/apps/wr/.env.mindone +++ b/apps/wr/.env.mindone @@ -5,6 +5,7 @@ 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:11301 +NEXT_PUBLIC_SUB_DOMAIN = http://con.mind-one.co.kr:13000 NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000 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 diff --git a/apps/wr/.env.production b/apps/wr/.env.production index 27c562dee..934f1e6a5 100644 --- a/apps/wr/.env.production +++ b/apps/wr/.env.production @@ -15,7 +15,7 @@ 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_SEWERAGE_API_URL = http://apigateway.iipbs-was.svc.cluster.local:8000/sewerage-service -NEXT_PUBLIC_NGII_URL = http://172.29.143.87 +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_HASUDOINFO = https://www.hasudoinfo.or.kr NEXT_PUBLIC_HASUDOINFO_IN = http://10.101.163.102:8889 diff --git a/apps/wr/app/(map-layout)/map/mainMap/page.tsx b/apps/wr/app/(map-layout)/map/mainMap/page.tsx index 362d45cb8..927703ad9 100644 --- a/apps/wr/app/(map-layout)/map/mainMap/page.tsx +++ b/apps/wr/app/(map-layout)/map/mainMap/page.tsx @@ -21,7 +21,7 @@ import WrOverLay from "@/components/ui/map/wrOverLay/wrOverLay"; const Map = () => { - const {code, comMap ,rainMapDetile ,rswtMapDetile,getRuseMaplListReturn,pbnMapDetaile,ldrmntMapDetile,htdfMapDetile,coSttnMapDetail} = useWrStore((state) => state); + const { code, comMap, rainMapDetile, rswtMapDetile, getRuseMaplListReturn, pbnMapDetaile, ldrmntMapDetile, htdfMapDetile, coSttnMapDetail } = useWrStore((state) => state); const { isFeatured } = comMap; const [treeData, setTreeData] = useState([]); // 초기값을 객체 또는 null로 설정 const gisMaps = useRef<any | null>(null); @@ -93,7 +93,6 @@ const Map = () => { return; } - // 항상 같은 select 객체 사용 const select = GisApp.Module.select as any; if (!select) return; @@ -109,7 +108,6 @@ const Map = () => { const layerName = getWrLayerNm(data); const featurePk = getWrFeaturePk(layerName ?? ""); - // 재이용시설 if (featurePk) { const lastPk = @@ -254,139 +252,139 @@ const Map = () => { const OVERLAY_MIN_ZOOM = 16; -const OVERLAY_COORD_PRECISION = 5; -const OVERLAY_MAX_PER_VIEW = 800; + const OVERLAY_COORD_PRECISION = 5; + const OVERLAY_MAX_PER_VIEW = 800; -const overlayManagersRef = useRef<any[]>([]); -const overlayActiveRef = useRef(false); -const moveEndDebounceRef = useRef<number | null>(null); + const overlayManagersRef = useRef<any[]>([]); + const overlayActiveRef = useRef(false); + const moveEndDebounceRef = useRef<number | null>(null); -// 반환: Promise<HTMLDivElement | null> -const overlayFormatter = async (feature: any, props: any) => { - const p = props || {}; - const featureId = - feature.getId?.() ?? p?.id ?? p?.fid ?? p?.ftrIdn ?? ''; - if (!featureId) return null; + // 반환: Promise<HTMLDivElement | null> + const overlayFormatter = async (feature: any, props: any) => { + const p = props || {}; + const featureId = + feature.getId?.() ?? p?.id ?? p?.fid ?? p?.ftrIdn ?? ''; + if (!featureId) return null; - const tableId = String(featureId).split('.')[0]??''; - const fcltCd = getFeatureCd(props, tableId); + const tableId = String(featureId).split('.')[0] ?? ''; + const fcltCd = getFeatureCd(props, tableId); - if (!fcltCd) return null; + if (!fcltCd) return null; - let data: any = null; - let title = ''; + let data: any = null; + let title = ''; - if (tableId === 'tb_il_swage_prcs_fclt') { data = await pbnMapDetaile(fcltCd); title = '하수처리시설'; } - else if (tableId === 'tb_wr_bdst_ruse_fclt') { data = await getRuseMaplListReturn(fcltCd); title = '하수재이용시설'; } - else if (tableId === 'tb_wr_rnwtr_utztn_fclt'){ data = await rainMapDetile(fcltCd); title = '빗물이용시설'; } - else if (tableId === 'tb_wr_rswtr_fclt') { data = await rswtMapDetile(fcltCd); title = '중수도시설'; } - else if (tableId === 'tb_wr_htdfl_fclt') { data = await htdfMapDetile(fcltCd); title = '온배수시설'; } - else if (tableId === 'tb_wr_ruse_fclt_dmd_co'){ data = await coSttnMapDetail(fcltCd); title = '수요처'; } - else if (tableId === 'tb_wr_tmpr_dmd_co') { data = await ldrmntMapDetile(fcltCd); title = '잠재수요처'; } + if (tableId === 'tb_il_swage_prcs_fclt') { data = await pbnMapDetaile(fcltCd); title = '하수처리시설'; } + else if (tableId === 'tb_wr_bdst_ruse_fclt') { data = await getRuseMaplListReturn(fcltCd); title = '하수재이용시설'; } + else if (tableId === 'tb_wr_rnwtr_utztn_fclt') { data = await rainMapDetile(fcltCd); title = '빗물이용시설'; } + else if (tableId === 'tb_wr_rswtr_fclt') { data = await rswtMapDetile(fcltCd); title = '중수도시설'; } + else if (tableId === 'tb_wr_htdfl_fclt') { data = await htdfMapDetile(fcltCd); title = '온배수시설'; } + else if (tableId === 'tb_wr_ruse_fclt_dmd_co') { data = await coSttnMapDetail(fcltCd); title = '수요처'; } + else if (tableId === 'tb_wr_tmpr_dmd_co') { data = await ldrmntMapDetile(fcltCd); title = '잠재수요처'; } - const empty = - data == null || - (Array.isArray(data) && data.length === 0) || - (typeof data === 'object' && !Array.isArray(data) && Object.keys(data).length === 0); + const empty = + data == null || + (Array.isArray(data) && data.length === 0) || + (typeof data === 'object' && !Array.isArray(data) && Object.keys(data).length === 0); - if (empty) return null; + if (empty) return null; - const div = document.createElement('div'); - const root = createRoot(div); - (div as any).__dispose = () => { try { root.unmount(); } catch {} }; - root.render(<WrOverLay title={title} data={data} code={code} />); - return div; // ← 데이터 있을 때만 HTMLElement 반환 -}; -const destroyAllManagers = () => { - overlayManagersRef.current.forEach(m => { try { m.destroy(); } catch {} }); - overlayManagersRef.current = []; - overlayActiveRef.current = false; -}; + const div = document.createElement('div'); + const root = createRoot(div); + (div as any).__dispose = () => { try { root.unmount(); } catch { } }; + root.render(<WrOverLay title={title} data={data} code={code} />); + return div; // ← 데이터 있을 때만 HTMLElement 반환 + }; + const destroyAllManagers = () => { + overlayManagersRef.current.forEach(m => { try { m.destroy(); } catch { } }); + overlayManagersRef.current = []; + overlayActiveRef.current = false; + }; -const mountOverlayManagers = () => { - const map = gisMaps.current; - if (!map) return; + const mountOverlayManagers = () => { + const map = gisMaps.current; + if (!map) return; - // 이전 것 정리 - destroyAllManagers(); + // 이전 것 정리 + destroyAllManagers(); - // 체크된 벡터 레이어만 골라 매니저 생성 - const checkedSet = new Set(checkedLayers.map(String)); - const layers: any[] = map.getLayers().getArray(); - const targets = layers.filter((layer: any) => { - const src = layer.getSource?.(); - const isVector = src && typeof src.getFeatures === "function"; - const id = String(layer.get?.("lyrSn") ?? layer.get?.("id") ?? ""); - return isVector && layer.getVisible?.() && checkedSet.has(id); - }); - - targets.forEach((layer: any) => { - const mgr = createOverlayManager(map, layer, overlayFormatter, { - coordPrecision: OVERLAY_COORD_PRECISION, - maxOverlaysPerViewport: OVERLAY_MAX_PER_VIEW, + // 체크된 벡터 레이어만 골라 매니저 생성 + const checkedSet = new Set(checkedLayers.map(String)); + const layers: any[] = map.getLayers().getArray(); + const targets = layers.filter((layer: any) => { + const src = layer.getSource?.(); + const isVector = src && typeof src.getFeatures === "function"; + const id = String(layer.get?.("lyrSn") ?? layer.get?.("id") ?? ""); + return isVector && layer.getVisible?.() && checkedSet.has(id); }); - mgr.mount(); // 리스너 1회 등록 - mgr.enable(); // 표시 + 1회 sync - overlayManagersRef.current.push(mgr); - }); - overlayActiveRef.current = overlayManagersRef.current.length > 0; -}; + targets.forEach((layer: any) => { + const mgr = createOverlayManager(map, layer, overlayFormatter, { + coordPrecision: OVERLAY_COORD_PRECISION, + maxOverlaysPerViewport: OVERLAY_MAX_PER_VIEW, + }); + mgr.mount(); // 리스너 1회 등록 + mgr.enable(); // 표시 + 1회 sync + overlayManagersRef.current.push(mgr); + }); -useEffect(() => { - const map = gisMaps.current; - if (!map) return; - - const z = map.getView()?.getZoom?.() ?? 0; - if (z < OVERLAY_MIN_ZOOM) { - // 저줌에선 그냥 안 보이게만 - overlayManagersRef.current.forEach(m => { try { m.disable({ clear:true, delayed:true }); } catch {} }); - return; - } - mountOverlayManagers(); - - return () => { destroyAllManagers(); }; -}, [checkedLayers]); - - -useEffect(() => { - const map = gisMaps.current; - if (!map) return; - - const onMoveStart = () => { - // 숨기기만 (리스너/DOM 대량 제거 X) - overlayManagersRef.current.forEach(m => { try { m.disable({ clear:false }); } catch {} }); - if (moveEndDebounceRef.current) { clearTimeout(moveEndDebounceRef.current); moveEndDebounceRef.current = null; } + overlayActiveRef.current = overlayManagersRef.current.length > 0; }; - const onMoveEnd = () => { + useEffect(() => { + const map = gisMaps.current; + if (!map) return; + const z = map.getView()?.getZoom?.() ?? 0; - const active = overlayActiveRef.current; + if (z < OVERLAY_MIN_ZOOM) { + // 저줌에선 그냥 안 보이게만 + overlayManagersRef.current.forEach(m => { try { m.disable({ clear: true, delayed: true }); } catch { } }); + return; + } + mountOverlayManagers(); - if (moveEndDebounceRef.current) clearTimeout(moveEndDebounceRef.current); - moveEndDebounceRef.current = window.setTimeout(() => { - if (z < OVERLAY_MIN_ZOOM) { - if (active) overlayManagersRef.current.forEach(m => { try { m.disable({ clear:true, delayed:true }); } catch {} }); - return; - } - if (!active) { - mountOverlayManagers(); - } else { - // 다시 보이고 1회만 동기화 - overlayManagersRef.current.forEach(m => { try { m.enable(); m.syncOverlays(); } catch {} }); - } - }, 180) as unknown as number; - }; + return () => { destroyAllManagers(); }; + }, [checkedLayers]); - map.on("movestart", onMoveStart); - map.on("moveend", onMoveEnd); - return () => { - map.un("movestart", onMoveStart); - map.un("moveend", onMoveEnd); - if (moveEndDebounceRef.current) { clearTimeout(moveEndDebounceRef.current); moveEndDebounceRef.current = null; } - }; -}, [checkedLayers]); + + useEffect(() => { + const map = gisMaps.current; + if (!map) return; + + const onMoveStart = () => { + // 숨기기만 (리스너/DOM 대량 제거 X) + overlayManagersRef.current.forEach(m => { try { m.disable({ clear: false }); } catch { } }); + if (moveEndDebounceRef.current) { clearTimeout(moveEndDebounceRef.current); moveEndDebounceRef.current = null; } + }; + + const onMoveEnd = () => { + const z = map.getView()?.getZoom?.() ?? 0; + const active = overlayActiveRef.current; + + if (moveEndDebounceRef.current) clearTimeout(moveEndDebounceRef.current); + moveEndDebounceRef.current = window.setTimeout(() => { + if (z < OVERLAY_MIN_ZOOM) { + if (active) overlayManagersRef.current.forEach(m => { try { m.disable({ clear: true, delayed: true }); } catch { } }); + return; + } + if (!active) { + mountOverlayManagers(); + } else { + // 다시 보이고 1회만 동기화 + overlayManagersRef.current.forEach(m => { try { m.enable(); m.syncOverlays(); } catch { } }); + } + }, 180) as unknown as number; + }; + + map.on("movestart", onMoveStart); + map.on("moveend", onMoveEnd); + return () => { + map.un("movestart", onMoveStart); + map.un("moveend", onMoveEnd); + if (moveEndDebounceRef.current) { clearTimeout(moveEndDebounceRef.current); moveEndDebounceRef.current = null; } + }; + }, [checkedLayers]); return ( <div className="bg-container"> <Gis diff --git a/apps/wr/app/(with-layout)/matMng/coMat/ruseCaseMng/page.tsx b/apps/wr/app/(with-layout)/matMng/coMat/ruseCaseMng/page.tsx index afea9d894..a64c57364 100644 --- a/apps/wr/app/(with-layout)/matMng/coMat/ruseCaseMng/page.tsx +++ b/apps/wr/app/(with-layout)/matMng/coMat/ruseCaseMng/page.tsx @@ -24,6 +24,7 @@ import { Button, Label } from "@common/components/ui"; import { useEffect, useState } from "react"; import toast, { Toaster } from "react-hot-toast"; import { useTranslation } from "react-i18next"; +import * as XLSX from "xlsx"; export default function RuseCaseMng() { const { code, ruseCaseMng, selectRuseCaseMng, ruseCaseMngInit } = useWrStore( @@ -76,7 +77,9 @@ export default function RuseCaseMng() { }; useEffect(() => { - if (excelList !== undefined && excelList.length === 0) { + if (!excelList) return; + + if (excelList.length === 0) { toast.error(t("excelDownErrMsg")); return; } @@ -86,74 +89,93 @@ export default function RuseCaseMng() { } // 받은 파일 바인너리 처리 const blobRet = await response.arrayBuffer(); - if (excelList) { - const excelData1 = excelList.map((item) => { - return [ - item.ruseBizNm, - item.bizBgngDate, - item.bizEndDate, - item.splyFcltKnd, - item.splyFcltNm, - item.splyFcltCpct, - item.dmdFcltNm, - item.dmdFcltAddr, - item.ruseFcltCpct, - item.ruseFcltPplLen, - item.ruseFcltUsg, - item.ruseFcltSplyCpct, - item.ruseFcltPrcsMsrc, - item.grnteWqUsg, - item.grnteWqGrssClfg, - item.grnteWqCombResdch, - item.grnteWqTrbt, - item.grnteWqBod, - item.grnteWqSmll, - item.grnteWqCrmty, - item.grnteWqTn, - item.grnteWqTp, - item.grnteWqPh, - item.grnteWqClIonQnt, - item.grnteWqCndctStat, - item.grnteWqRmrk, - item.reqBizGrssCst, - item.reqBizReuseFcltCst, - item.reqBizPplCst, - item.reqBizArchCst, - item.reqBizMchnCst, - item.reqBizMtrngCst, - item.reqBizGisCst, - item.reqBizCctvCst, - item.reqBizCmcnCst, - item.reqBizWstCst, - item.reqBizDesignCst, - item.reqBizSpvsCst, - item.reqBizSbsdCst, - item.reqBizEtc, - item.prcmPlanGrssCst, - item.prcmPlanNlep, - item.prcmPlanLcep, - item.prcmPlanBrdnCst, - item.prcmPlanEcqrRt, - item.clclnGrssCst, - item.clclnNlep, - item.clclnLcep, - item.clclnBrdnCst, - item.clclnEcqrRt, - ]; - }); - const sheetParameter: SheetParameter[] = [ - { - data: excelData1, - headerNum: 2, - headerRange: "A1:AX1", - sheetName: "물재이용사례", - startCol: "A", - }, + const replaceHeaderText = (text: string) => + text + .replaceAll("사업시작일", "사업시작년") + .replaceAll("사업종료일", "사업종료년"); + + const wb = XLSX.read(blobRet, { type: "array" }); + wb.SheetNames.forEach((sheetName) => { + const sheet = wb.Sheets[sheetName]; + if (!sheet) return; + + Object.keys(sheet).forEach((addr) => { + if (addr.startsWith("!")) return; + const cell = sheet[addr] as XLSX.CellObject | undefined; + if (!cell || typeof cell.v !== "string") return; + const next = replaceHeaderText(cell.v); + if (next !== cell.v) cell.v = next; + }); + }); + const updatedBlob = XLSX.write(wb, { bookType: "xlsx", type: "array" }); + + const excelData1 = excelList.map((item) => { + return [ + item.ruseBizNm, + item.bizBgngDate, + item.bizEndDate, + item.splyFcltKnd, + item.splyFcltNm, + item.splyFcltCpct, + item.dmdFcltNm, + item.dmdFcltAddr, + item.ruseFcltCpct, + item.ruseFcltPplLen, + item.ruseFcltUsg, + item.ruseFcltSplyCpct, + item.ruseFcltPrcsMsrc, + item.grnteWqUsg, + item.grnteWqGrssClfg, + item.grnteWqCombResdch, + item.grnteWqTrbt, + item.grnteWqBod, + item.grnteWqSmll, + item.grnteWqCrmty, + item.grnteWqTn, + item.grnteWqTp, + item.grnteWqPh, + item.grnteWqClIonQnt, + item.grnteWqCndctStat, + item.grnteWqRmrk, + item.reqBizGrssCst, + item.reqBizReuseFcltCst, + item.reqBizPplCst, + item.reqBizArchCst, + item.reqBizMchnCst, + item.reqBizMtrngCst, + item.reqBizGisCst, + item.reqBizCctvCst, + item.reqBizCmcnCst, + item.reqBizWstCst, + item.reqBizDesignCst, + item.reqBizSpvsCst, + item.reqBizSbsdCst, + item.reqBizEtc, + item.prcmPlanGrssCst, + item.prcmPlanNlep, + item.prcmPlanLcep, + item.prcmPlanBrdnCst, + item.prcmPlanEcqrRt, + item.clclnGrssCst, + item.clclnNlep, + item.clclnLcep, + item.clclnBrdnCst, + item.clclnEcqrRt, ]; - // 버퍼에 있는 엑셀데이터 전달 후 json으로 변환 - await filePickedBlobArray(blobRet, "물재이용사례현황", sheetParameter); - } + }); + + const sheetParameter: SheetParameter[] = [ + { + data: excelData1, + headerNum: 2, + headerRange: "A1:AX1", + sheetName: "물재이용사례", + startCol: "A", + }, + ]; + // 버퍼에 있는 엑셀데이터 전달 후 json으로 변환 + await filePickedBlobArray(updatedBlob, "물재이용사례현황", sheetParameter); }); }, [excelList]); @@ -299,8 +321,12 @@ export default function RuseCaseMng() { }} resetPageNo={resetPage} /> + <span className="text-2xl font-bold text-gray-500"> + ☞ 본 사례의 사업비는 재원협의 단계의 정보이므로, 최종확정된 비용이 아닙니다. + </span> </div> </ListDiv> + </div> </div> </ContentsDiv> diff --git a/apps/wr/app/(with-layout)/ruseFcltMng/fcltReg/pbnstReg/page.tsx b/apps/wr/app/(with-layout)/ruseFcltMng/fcltReg/pbnstReg/page.tsx index d5d0e9b33..f6d83e29c 100644 --- a/apps/wr/app/(with-layout)/ruseFcltMng/fcltReg/pbnstReg/page.tsx +++ b/apps/wr/app/(with-layout)/ruseFcltMng/fcltReg/pbnstReg/page.tsx @@ -1,3 +1,19 @@ +/** + * 재이용시설 등록 - 하수재이용 시설등록 + * + * - layout.tsx에 의해 공통 레이아웃이 적용됨 + * - with-layout 적용 + * - 하수처리시설에 하수재이용 시설을 등록 한다. + * - form으로 시설정보 및 차수별 정보의 컴포넌트 데이터를 관리한다 + * indexMng: 시설정보 + * indexBiz: 차수별 정보 + * 로 하수재이용 시설에 등록된 시설 및 차수별 정보를 관리한다. + * 🔗 사용 API + * 하수재이용시설을 등록 /reuseFc/pubFc + * 첨부파일 삭제 /attach-service/api/v1/atfl/attachments/atflCd// + * 첨부파일 다운로드 /attach-service/api/v1/atflcd/download/ + * */ + "use client"; import { ContentsDiv, @@ -35,18 +51,6 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; import { createDefaultPbnstItem } from "./utilsZod"; import { useTranslation } from "react-i18next"; -/** - * 재이용시설 등록 - 하수재이용 시설등록 - * - * - layout.tsx에 의해 공통 레이아웃이 적용됨 - * - with-layout 적용 - * - 하수처리시설에 하수재이용 시설을 등록 한다. - * - form으로 시설정보 및 차수별 정보의 컴포넌트 데이터를 관리한다 - * indexMng: 시설정보 - * indexBiz: 차수별 정보 - * 로 하수재이용 시설에 등록된 시설 및 차수별 정보를 관리한다. - * */ - export default function PublicReuseFaciltyInsert() { const { auth, @@ -104,7 +108,7 @@ export default function PublicReuseFaciltyInsert() { useEffect(() => { if (useInitRef.current == true && laeSeCd !== undefined) { - pubHasuInit("1", laeSeCd); + pubHasuInit("1", laeSeCd); //page: string, sggOrCity: string } }, [useInitRef.current, laeSeCd]); diff --git a/apps/wr/app/(with-layout)/ruseFcltMng/fcltSttn/htdflSttn/page.tsx b/apps/wr/app/(with-layout)/ruseFcltMng/fcltSttn/htdflSttn/page.tsx index beaa71569..ea4b49924 100644 --- a/apps/wr/app/(with-layout)/ruseFcltMng/fcltSttn/htdflSttn/page.tsx +++ b/apps/wr/app/(with-layout)/ruseFcltMng/fcltSttn/htdflSttn/page.tsx @@ -1,5 +1,7 @@ +/** + * 온배수시설 현황 + */ "use client"; - import { useWrStore } from "@/app/store"; import { btnClass } from "@/app/store/common/code"; import type { @@ -34,11 +36,9 @@ import toast, { Toaster } from "react-hot-toast"; import { useTranslation } from "react-i18next"; export default function HtdflSttn() { - const { code, htdflSttn, htdflSttnSearch, initHtdfl } = useWrStore( - (state) => state - ); + const { auth, code, htdflSttn, htdflSttnSearch, initHtdfl } = useWrStore((state) => state); + const { laeSeCd } = auth; // 지자체구분코드 const { htdflSttnRes, htdflSttnResCount } = htdflSttn; - const isDownloadTriggered = useRef(false); const { t } = useTranslation('base'); const [pageNation, setPageNation] = useState(1); @@ -57,6 +57,7 @@ export default function HtdflSttn() { // }>({ isOpen: false, chBuilding: [] }); const [fcltNm, setFcltNm] = useState<string>(""); + console.log("laeSeCd : ", laeSeCd); useEffect(() => { htdflSttnSearch(false, pageNation, htdflSttnSize, String(getConValue()), fcltNm, code.cityCd, code.sggCd); isResetPage(false) diff --git a/apps/wr/app/(with-layout)/ruseFcltMng/fcltSttn/pbnstSttn/page.tsx b/apps/wr/app/(with-layout)/ruseFcltMng/fcltSttn/pbnstSttn/page.tsx index ca0b04f31..2a2340253 100644 --- a/apps/wr/app/(with-layout)/ruseFcltMng/fcltSttn/pbnstSttn/page.tsx +++ b/apps/wr/app/(with-layout)/ruseFcltMng/fcltSttn/pbnstSttn/page.tsx @@ -1,3 +1,12 @@ +/** + * 하수재이용 시설 현황 + * 재이용시설 현황 - 하수재이용시설 현황 + * - layout.tsx에 의해 공통 레이아웃이 적용됨 + * - with-layout 적용 + * 하수처리시설. 하수재이용시설 , 하수재이용시설 실적에 대한 정보를 확인한다. + * 모달 : 테이블의 헤더를 원하는대로 조절해서 볼수 있다. + * 클릭시 해당 재이용ㅇ + * */ "use client"; import { useWrStore } from "@/app/store"; import { btnClass } from "@/app/store/common/code"; @@ -24,15 +33,7 @@ import { useCallback, useEffect, useRef, useState } from "react"; import toast, { Toaster } from "react-hot-toast"; import { useTranslation } from "react-i18next"; -/** - * 하수재이용 시설 현황 - * 재이용시설 현황 - 하수재이용시설 현황 - * - layout.tsx에 의해 공통 레이아웃이 적용됨 - * - with-layout 적용 - * 하수처리시설. 하수재이용시설 , 하수재이용시설 실적에 대한 정보를 확인한다. - * 모달 : 테이블의 헤더를 원하는대로 조절해서 볼수 있다. - * 클릭시 해당 재이용ㅇ - * */ + export default function PbnstSttn() { const { code, diff --git a/apps/wr/app/(with-layout)/ruseFcltMng/fcltSttn/rainSttn/page.tsx b/apps/wr/app/(with-layout)/ruseFcltMng/fcltSttn/rainSttn/page.tsx index f5ec6e8c8..46f1fa0c4 100644 --- a/apps/wr/app/(with-layout)/ruseFcltMng/fcltSttn/rainSttn/page.tsx +++ b/apps/wr/app/(with-layout)/ruseFcltMng/fcltSttn/rainSttn/page.tsx @@ -1,3 +1,6 @@ +/** + * 빗물재이용시설 현황 + */ "use client"; import { useWrStore } from "@/app/store"; import { btnClass } from "@/app/store/common/code"; diff --git a/apps/wr/app/(with-layout)/ruseFcltMng/fcltSttn/rswtSttn/page.tsx b/apps/wr/app/(with-layout)/ruseFcltMng/fcltSttn/rswtSttn/page.tsx index 7cb81d209..268ef8a35 100644 --- a/apps/wr/app/(with-layout)/ruseFcltMng/fcltSttn/rswtSttn/page.tsx +++ b/apps/wr/app/(with-layout)/ruseFcltMng/fcltSttn/rswtSttn/page.tsx @@ -1,3 +1,6 @@ +/** + * 중수도시설 현황 + */ "use client"; import { useWrStore } from "@/app/store"; import { btnClass } from "@/app/store/common/code"; diff --git a/apps/wr/app/(with-layout)/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx b/apps/wr/app/(with-layout)/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx index aea5c4b39..0fe594630 100644 --- a/apps/wr/app/(with-layout)/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx +++ b/apps/wr/app/(with-layout)/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx @@ -94,7 +94,7 @@ export default function Page() { const cellClick = () => { if (searchYear) - if (selFcltDiv === "RNB") { + if (selFcltDiv === "RNB") { // 빗물 if (fcltNm) { rainChkNmSearch( rainPageNation, @@ -107,7 +107,7 @@ export default function Page() { } else { rainChkInit(rainPageNation, rainPageSize, searchYear, laeSeCd); } - } else { + } else { // 중수도 if (fcltNm) { rswtChkNmSearch( rainPageNation, diff --git a/apps/wr/app/(without-layout)/stats/hasuStat/page.tsx b/apps/wr/app/(without-layout)/stats/hasuStat/page.tsx index 34ce031e1..d6e9ff601 100644 --- a/apps/wr/app/(without-layout)/stats/hasuStat/page.tsx +++ b/apps/wr/app/(without-layout)/stats/hasuStat/page.tsx @@ -20,20 +20,20 @@ export default function HasuStatPage() { const data = await res.json(); const ip: string = data?.ip || ""; if (didCancel) return; - //alert("Client IP:" + ip); + console.log("Client IP:" + ip); const is10 = /^10\./.test(ip); const is172 = /^172\./.test(ip); - setStatus(is10||is172 ? "redirect-internal" : "redirect-root"); + setStatus(is10 || is172 ? "redirect-internal" : "redirect-root"); // Use location.replace so user can't go back to this page - if (is10||is172) { + if (is10 || is172) { // redirect to internal IP // alert(process.env.NEXT_PUBLIC_HASUDOINFO_IN +'/cms/lay1/WS10000014/program.do'); - window.open(process.env.NEXT_PUBLIC_HASUDOINFO_IN +'/cms/lay1/WS10000014/program.do', '_blank'); + window.open(process.env.NEXT_PUBLIC_HASUDOINFO_IN + '/cms/lay1/WS10000014/program.do', '_blank'); } else { // alert(process.env.NEXT_PUBLIC_HASUDOINFO +'/cms/lay1/WS10000014/program.do'); - window.open(process.env.NEXT_PUBLIC_HASUDOINFO +'/cms/lay1/WS10000014/program.do', '_blank'); + window.open(process.env.NEXT_PUBLIC_HASUDOINFO + '/cms/lay1/WS10000014/program.do', '_blank'); } window.history.back(); } catch { @@ -53,13 +53,13 @@ export default function HasuStatPage() { }, []); return ( - <div style={{padding:20,fontFamily:'Noto Sans KR, sans-serif'}}> + <div style={{ padding: 20, fontFamily: 'Noto Sans KR, sans-serif' }}> {status === "checking" && <div>접속 IP 확인 중...</div>} {status === "redirect-internal" && <div>내부망으로 이동합니다...</div>} {status === "redirect-root" && <div>외부에서 접속하여 루트로 이동합니다...</div>} {status === "error-fallback" && ( <div> - 문제가 발생하여 루트로 이동합니다. 이동하지 않으면 <Link href="/">여기</Link>를 클릭하세요. + 문제가 발생하여 루트로 이동합니다. 이동하지 않으면 <Link href="/">여기</Link>를 클릭하세요. </div> )} </div> diff --git a/apps/wr/app/store/common/auth/index.tsx b/apps/wr/app/store/common/auth/index.tsx index 7b647d5e7..209b1fb8a 100644 --- a/apps/wr/app/store/common/auth/index.tsx +++ b/apps/wr/app/store/common/auth/index.tsx @@ -10,7 +10,7 @@ export type authState = { regAuthrtYn: string; mdfcnAuthrtYn: string; delAuthrtYn: string; - laeSeCd: string; + laeSeCd: string; // 지자체구분코드 myUUID?: string; }; }; @@ -72,6 +72,7 @@ export const authActionsExport: (set: any, get: any) => authActions = ( return; } const infoMe = await getUserInfo(); + console.log(infoMe); const isLaeSeCd = infoMe.laeSeCd.slice(0, 2) === "G3" ? infoMe.laeSeCd.slice(2) : "0"; diff --git a/apps/wr/app/store/matDmd/ruseCaseMng/headList.tsx b/apps/wr/app/store/matDmd/ruseCaseMng/headList.tsx index 8ed65ee7d..c201f5d28 100644 --- a/apps/wr/app/store/matDmd/ruseCaseMng/headList.tsx +++ b/apps/wr/app/store/matDmd/ruseCaseMng/headList.tsx @@ -11,14 +11,14 @@ export const ruseCaseHeadListExport: TableUpperProps[] = [ }, { id: "bizBgngDate", - title: "사업시작일", + title: "사업시작년", visible: true, row: 0, rowSpan: 2, }, { id: "bizEndDate", - title: "사업종료일", + title: "사업종료년", visible: true, row: 0, rowSpan: 2, diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/index.tsx index b1e0daf82..d595a0624 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/index.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/index.tsx @@ -118,7 +118,7 @@ const hasuListFetch = async ( if (searchTxt) { url = url + `/${searchTxt}`; } - const rainRes = await postFectch(url, data, fetchType.COMMON); + const rainRes = await postFectch(url, data, fetchType.COMMON); // fo const hasuList = rainRes.data.map((item: any) => { return { @@ -176,7 +176,7 @@ export const pubFacActionsExport: (set: any) => pubFacActions = (set: any) => { // 하수처리시설을 조회한다(운영실태) pubHasuInit: async (page: string, sggOrCity: string) => { try { - const pbnstHasuRes = await hasuListFetch(page, sggOrCity); + const pbnstHasuRes = await hasuListFetch(page, sggOrCity); // /prcsFclt/ruseInfo return set((state: pubFacState) => ({ ...state, pbnst: { diff --git a/apps/wr/components/lib/func/TableData.tsx b/apps/wr/components/lib/func/TableData.tsx index 1a66802f5..0c613bed0 100644 --- a/apps/wr/components/lib/func/TableData.tsx +++ b/apps/wr/components/lib/func/TableData.tsx @@ -190,12 +190,10 @@ export const chRowspanTable = ( value={formatCommaNumber(value) || ""} disabled={!selectedIds.includes(row.chkgdnSn)} maxLength={numSize} - onInput={(e: React.ChangeEvent<HTMLInputElement>) => { - if ( - head.type === "number" && - e.target.value.length > numSize - ) { - e.target.value = e.target.value.slice(0, numSize); + onInput={(e: React.FormEvent<HTMLInputElement>) => { + const el = e.currentTarget; + if (head.type === "number" && el.value.length > numSize) { + el.value = el.value.slice(0, numSize); } }} /> @@ -225,12 +223,10 @@ export const chRowspanTable = ( value={value || ""} disabled={!selectedIds.includes(row.chkgdnSn)} maxLength={numSize} - onInput={(e: React.ChangeEvent<HTMLInputElement>) => { - if ( - head.type === "number" && - e.target.value.length > numSize - ) { - e.target.value = e.target.value.slice(0, numSize); + onInput={(e: React.FormEvent<HTMLInputElement>) => { + const el = e.currentTarget; + if (head.type === "number" && el.value.length > numSize) { + el.value = el.value.slice(0, numSize); } }} /> diff --git a/apps/wr/components/lib/func/mapUtil.ts b/apps/wr/components/lib/func/mapUtil.ts index a39bf17d7..c49dea69f 100644 --- a/apps/wr/components/lib/func/mapUtil.ts +++ b/apps/wr/components/lib/func/mapUtil.ts @@ -1,22 +1,22 @@ import { formatCommaNumber, formatPhoneNumber } from "./commom"; const wrLayerList = [ - { text: "tb_il_swage_prcs_fclt", val: "bscFctCd" }, - { text: "tb_wr_bdst_ruse_fclt", val: "pbnstRuseFcltId" }, - { text: "tb_wr_rnwtr_utztn_fclt", val: "rainyObj" }, - { text: "tb_wr_rswtr_fclt", val: "rswtrObj" }, - { text: "tb_wr_htdfl_fclt", val: "htdflObj" }, - { text: "tb_wr_ruse_fclt_dmd_co", val: "dmdInfoId" }, - { text: "tb_wr_tmpr_dmd_co", val: "fctryMngSn" }, + { text: "tb_il_swage_prcs_fclt", val: "bscFctCd" }, //하수처리시설 + { text: "tb_wr_bdst_ruse_fclt", val: "pbnstRuseFcltId" }, //하수재이용시설 + { text: "tb_wr_rnwtr_utztn_fclt", val: "rainyObj" }, //빗물이용시설 + { text: "tb_wr_rswtr_fclt", val: "rswtrObj" }, //중수도시설 + { text: "tb_wr_htdfl_fclt", val: "htdflObj" }, //온배수시설 + { text: "tb_wr_ruse_fclt_dmd_co", val: "dmdInfoId" }, //수요처 + { text: "tb_wr_tmpr_dmd_co", val: "fctryMngSn" }, //잠재수요처 ]; const wrFeaturePK = [ - { text: "bscFctCd", val: "tb_il_swage_prcs_fclt" }, - { text: "searchPk", val: "tb_wr_bdst_ruse_fclt" }, - { text: "bdstInfoSn", val: "tb_wr_rnwtr_utztn_fclt" }, - { text: "bdstInfoSn", val: "tb_wr_rswtr_fclt" }, - { text: "bdstInfoSn", val: "tb_wr_htdfl_fclt" }, - { text: "dmdInfoId", val: "tb_wr_ruse_fclt_dmd_co" }, - { text: "fctryMngSn", val: "tb_wr_tmpr_dmd_co" }, + { text: "bscFctCd", val: "tb_il_swage_prcs_fclt" }, //하수처리시설 + { text: "searchPk", val: "tb_wr_bdst_ruse_fclt" }, //하수재이용시설 + { text: "bdstInfoSn", val: "tb_wr_rnwtr_utztn_fclt" }, //빗물이용시설 + { text: "bdstInfoSn", val: "tb_wr_rswtr_fclt" }, //중수도시설 + { text: "bdstInfoSn", val: "tb_wr_htdfl_fclt" }, //온배수시설 + { text: "dmdInfoId", val: "tb_wr_ruse_fclt_dmd_co" }, //수요처 + { text: "fctryMngSn", val: "tb_wr_tmpr_dmd_co" }, //잠재수요처 ]; // { text: "하수처리시설", val: "tb_il_swage_prcs_fclt" }, @@ -29,18 +29,18 @@ const wrFeaturePK = [ export const getFeatureCd = (featurePropertise: any, layerNm: string) => { const featureCd = - layerNm === "tb_wr_bdst_ruse_fclt" + layerNm === "tb_wr_bdst_ruse_fclt" //하수재이용시설 ? featurePropertise?.swtrmt_fclt_cd : [ - "tb_wr_rswtr_fclt", - "tb_wr_htdfl_fclt", - "tb_wr_rnwtr_utztn_fclt", - "tb_wr_tmpr_dmd_co", - ].includes(layerNm) + "tb_wr_rswtr_fclt", // 중수도시설 + "tb_wr_htdfl_fclt", // 온배수시설 + "tb_wr_rnwtr_utztn_fclt", // 빗물이용시설 + "tb_wr_tmpr_dmd_co", // 잠재수요처 + ].includes(layerNm) ? featurePropertise?.bdst_nm - : layerNm === "tb_il_swage_prcs_fclt" + : layerNm === "tb_il_swage_prcs_fclt" //하수처리시설 ? featurePropertise?.swtrmt_fclt_cd - : layerNm === "tb_wr_ruse_fclt_dmd_co" + : layerNm === "tb_wr_ruse_fclt_dmd_co" //수요처 ? featurePropertise?.bdst_info_sn : null; @@ -71,9 +71,9 @@ export const getSelectedLayers = (isFeature: any, gisMaps: any) => { let result = null; const layers = map ? map - .getLayers() - .getArray() - .filter((l: any) => l.getVisible()) + .getLayers() + .getArray() + .filter((l: any) => l.getVisible()) : []; // gisMap이 null인지 확인 후 레이어 가져오기 let featureLayer: { get: (key: string) => any } = { get: () => null }; @@ -119,12 +119,12 @@ export const getClickFeatureId = (data: any) => { } else if (layerName == "수요처") { //수요처 featureId = `${layerName}.${data.dmdInfoId.replaceAll("-", "")}`; - }else if (layerName == "잠재수요처") { + } else if (layerName == "잠재수요처") { //잠재수요처 featureId = data.id_ - } else { - + } else { + featureId = data.id_ } @@ -160,7 +160,7 @@ export function featurePopUpOpen( ); } -export const getOverLayObj = (fclType: string, data: any,code?:any) => { +export const getOverLayObj = (fclType: string, data: any, code?: any) => { let newObj = {}; let title = ""; @@ -169,11 +169,11 @@ export const getOverLayObj = (fclType: string, data: any,code?:any) => { if (fclType === "하수재이용시설") { newObj = { - "시설용량(톤/일)": formatCommaNumber(data?.fcltCpct) ??'확인불가', - "재이용률(%)": (data?.fcltCpct / data?.bscFctCpc)||0, + "시설용량(톤/일)": formatCommaNumber(data?.fcltCpct) ?? '확인불가', + "재이용률(%)": (data?.fcltCpct / data?.bscFctCpc) || 0, "장내용수(m³/년)": formatCommaNumber(data?.inUsage) ?? '확인불가', - "장외용수(m3/년)":formatCommaNumber(data?.outUsage) ?? '확인불가', - // 수요처: data?.reuseBuilding[0]?.rainyObj.pntfColArea ?? '확인불가', + "장외용수(m3/년)": formatCommaNumber(data?.outUsage) ?? '확인불가', + // 수요처: data?.reuseBuilding[0]?.rainyObj.pntfColArea ?? '확인불가', }; title = data?.bdstNm ?? '확인불가'; } else if (fclType === "하수처리시설") { @@ -184,9 +184,9 @@ export const getOverLayObj = (fclType: string, data: any,code?:any) => { "재이용시설 등록여부": data?.ruse ?? '확인불가', }; title = data?.bscFctNm ?? '확인불가'; - } else if (fclType === "빗물이용시설" ) { + } else if (fclType === "빗물이용시설") { newObj = { - 건물용도: code?.bdstUsgCd.find((item) => item.val=== String(data?.reuseBuilding[0].bdstUsgCd) )?.text ?? '확인불가', + 건물용도: code?.bdstUsgCd.find((item) => item.val === String(data?.reuseBuilding[0].bdstUsgCd))?.text ?? '확인불가', 설치완료일: data?.reuseBuilding[0]?.instlCmptnDt ?? '확인불가', 처리시설유무: data?.reuseBuilding[0]?.rainyObj.filtrTrtmfctYn ?? '확인불가', "처리시설용량(m³/일)": formatCommaNumber(data?.reuseBuilding[0]?.rainyObj.filtrTrtmfctCpct) ?? '확인불가', @@ -195,25 +195,25 @@ export const getOverLayObj = (fclType: string, data: any,code?:any) => { title = data?.bdstNm ?? '확인불가'; } else if (fclType === "중수도시설") { newObj = { - 건물용도: code?.bdstUsgCd.find((item) => item.val===String(data?.reuseBuilding[0].bdstUsgCd) )?.text ?? '확인불가', + 건물용도: code?.bdstUsgCd.find((item) => item.val === String(data?.reuseBuilding[0].bdstUsgCd))?.text ?? '확인불가', 설치완료일: data?.reuseBuilding[0]?.instlCmptnDt ?? '확인불가', - "주요 처리공정": data?.reuseBuilding[0]?.rswtrObj.filtrTrtmfctProcs ?? '확인불가', - 원수종류: code?.orgwtKndCd.find((item) => item.val === data?.reuseBuilding[0]?.rswtrObj.orgwtKndCd)?.text ?? '확인불가', + "주요 처리공정": data?.reuseBuilding[0]?.rswtrObj.filtrTrtmfctProcs ?? '확인불가', + 원수종류: code?.orgwtKndCd.find((item) => item.val === data?.reuseBuilding[0]?.rswtrObj.orgwtKndCd)?.text ?? '확인불가', //이용용도: isCode.filtrTrtmfctUsgCd data?.reuseBuilding[0]?.rswtrObj.altUswtrKndCd ?? '확인불가', }; title = data?.bdstNm ?? '확인불가'; } else if (fclType === "온배수시설") { newObj = { - '발전유형': data?.reuseBuilding[0]?.htdflObj.etgtType??'', - '설비용량': data?.reuseBuilding[0]?.htdflObj.fclCpct??'', - '담수화 여부': data?.reuseBuilding[0]?.htdflObj.plwtYn??'', + '발전유형': data?.reuseBuilding[0]?.htdflObj.etgtType ?? '', + '설비용량': data?.reuseBuilding[0]?.htdflObj.fclCpct ?? '', + '담수화 여부': data?.reuseBuilding[0]?.htdflObj.plwtYn ?? '', }; title = data?.bdstNm ?? '확인불가'; } else if (fclType === "수요처") { newObj = { - '사업자등록번호': data?.dmdInfoId??'', - '주소': data?.daddr??'', - '전화번호': formatPhoneNumber(data?.telno)??'', + '사업자등록번호': data?.dmdInfoId ?? '', + '주소': data?.daddr ?? '', + '전화번호': formatPhoneNumber(data?.telno) ?? '', }; title = data?.dmdCoNm ?? '확인불가'; diff --git a/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm.tsx b/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm.tsx index 6fd110297..d4e7fe940 100644 --- a/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm.tsx +++ b/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm.tsx @@ -35,10 +35,11 @@ export type FilesType = { file: File; }; +const diagramFileTypes = ".pdf,.jpg,.jpeg,.png,.bmp"; /** * 재이용시설 등록 - 하수재이용 시설등록 - * 재이용시설 등록 - 하수재이용 시설현황 + * 재이용시설 등록 - 하수재이용 시설현황 * - layout.tsx에 의해 공통 레이아웃이 적용됨 * - with-layout 적용 * - 하수처리시설에 하수재이용 시설 시설 정보를 등록한다. @@ -459,7 +460,7 @@ export function PbnstMngForm({ setFiles={(files: File[] | null) => { if (files) fileHandler(files, "relation"); }} - fileTypes=".pdf" + fileTypes={diagramFileTypes} onFileClick={(index) => { fileDownLoad( relationfiles?.find( @@ -497,7 +498,7 @@ export function PbnstMngForm({ setFiles={(files: File[] | null) => { if (files) fileHandler(files, "plan"); }} - fileTypes=".pdf" + fileTypes={diagramFileTypes} onFileClick={(index) => { fileDownLoad( planFiles?.find( diff --git a/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx b/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx index e931cf706..3a59edb49 100644 --- a/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx +++ b/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx @@ -13,6 +13,7 @@ import { chRowspanTable, rswtChkTableData, } from "@/components/lib/func/TableData"; +import { codeToText } from "@/components/lib/func/commom"; import UiPageNations from "@common/business_components/ui/table/uiPageNations"; import { Table, TableBody, TableHeader } from "@common/components/ui"; import { useCallback, useEffect, useState } from "react"; @@ -99,9 +100,16 @@ export default function RswtChkgdTable({ const tableTotalData = rswtChkTableData(rswtChkData); const makeRswtChkData = tableTotalData.map((item: any) => { const bud = tableData([item.building], { code }); + // 운영관리형태 코드 → 한글 텍스트로 치환 (빗물 화면과 동일한 표시) + const operMngCdText = + codeToText(item.operMngCd?.trim(), "rainRswtMng") ?? + codeToText(item.reuseBuilding?.operMngCd?.trim(), "rainRswtMng") ?? + item.operMngCd; + return { ...item, ...bud[0], + operMngCd: operMngCdText, rowspan: item.rswtrDetails?.length, }; }); diff --git a/apps/wr/next.config.ts b/apps/wr/next.config.ts index 709fd3cf6..6a892461e 100644 --- a/apps/wr/next.config.ts +++ b/apps/wr/next.config.ts @@ -1,7 +1,7 @@ import type { NextConfig } from "next"; -const subDomain = - process.env.NEXT_PUBLIC_PROXY_HOST || "http://localhost:3000"; +const domain = process.env.NEXT_PUBLIC_PROXY_HOST || "http://localhost:3000"; +const subDomain = process.env.NEXT_PUBLIC_SUB_DOMAIN || "http://localhost:13000"; const nextConfig: NextConfig = { output: "standalone", @@ -18,7 +18,7 @@ const nextConfig: NextConfig = { { type: "header", key: "referer", - value: "(.*)/pst(.*)", + value: "(.*)/cms(.*)", }, ], }, @@ -26,19 +26,23 @@ const nextConfig: NextConfig = { afterFiles: [ { source: "/sewerage-service/:path*", - destination: `${subDomain}/sewerage-service/:path*`, + destination: `${domain}/sewerage-service/:path*`, }, { source: "/reuse-service/:path*", - destination: `${subDomain}/reuse-service/:path*`, + destination: `${domain}/reuse-service/:path*`, }, { source: "/operation-service/:path*", - destination: `${subDomain}/operation-service/:path*`, + destination: `${domain}/operation-service/:path*`, }, { source: "/ngii-server/:path*", - destination: `${subDomain}/ngii-server/:path*`, + destination: `${domain}/ngii-server/:path*`, + }, + { + source: "/cms/:path*", + destination: `${subDomain}/cms/:path*`, }, ], fallback: [], diff --git a/packages/common/business_components/src/lib/utils.ts b/packages/common/business_components/src/lib/utils.ts index a5ef19350..1a87df14d 100644 --- a/packages/common/business_components/src/lib/utils.ts +++ b/packages/common/business_components/src/lib/utils.ts @@ -2,5 +2,5 @@ import { clsx, type ClassValue } from "clsx"; import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)); + return twMerge(clsx(inputs)); //clsx 결과 (단순 합치기):twMerge 결과 (Tailwind 충돌 제거) } diff --git a/packages/common/business_components/src/ui/excel/index.tsx b/packages/common/business_components/src/ui/excel/index.tsx index aedb32256..80acd9696 100644 --- a/packages/common/business_components/src/ui/excel/index.tsx +++ b/packages/common/business_components/src/ui/excel/index.tsx @@ -6,10 +6,15 @@ type ExcelDownloadProps = { fileName: string; sheetNameArray: string[]; headerRange?: string; + headers?: readonly string[]|undefined; decimalArray0? : number[]; decimalArray1? : number[]; decimalArray2? : number[]; decimalArray3? : number[]; + decimalRange0?: string; + decimalRange1?: string; + decimalRange2?: string; + decimalRange3?: string; } export const downloadExcel = ({ data, fileName = new Date().getTime().toString() + '.xlsx', sheetNameArray = ["Sheet1"], headerRange='A1:V3'}: ExcelDownloadProps) => { @@ -19,7 +24,29 @@ export const downloadExcel = ({ data, fileName = new Date().getTime().toString() excelStyle(XLSX.write(workbook, { bookType: "xlsx", type: "buffer" }), headerRange, fileName); }; -export const downloadExcelFormat = ({ data, fileName = new Date().getTime().toString() + '.xlsx', sheetNameArray = ["Sheet1"], headerRange='A1:V3', decimalArray0=[], decimalArray1=[], decimalArray2=[], decimalArray3=[]}: ExcelDownloadProps) => { +export const downloadExcelHeaders = ({ data, fileName = new Date().getTime().toString() + '.xlsx', sheetNameArray = ["Sheet1"], headerRange='A1:V3', headers}: ExcelDownloadProps) => { + const dataArray = Array.isArray(data) ? data : [data]; + // headers가 제공되면 열 순서를 강제함 + const worksheet = XLSX.utils.json_to_sheet(dataArray, headers ? { header: [...headers] } : undefined); + const workbook = XLSX.utils.book_new(); + XLSX.utils.book_append_sheet(workbook, worksheet, sheetNameArray[0]); + excelStyle(XLSX.write(workbook, { bookType: "xlsx", type: "buffer" }), headerRange, fileName); +}; + +export const downloadExcelFormat = ({ + data, + fileName = new Date().getTime().toString() + '.xlsx', + sheetNameArray = ["Sheet1"], + headerRange = 'A1:V3', + decimalArray0 = [], + decimalArray1 = [], + decimalArray2 = [], + decimalArray3 = [], + decimalRange0, + decimalRange1, + decimalRange2, + decimalRange3, +}: ExcelDownloadProps) => { const worksheet = XLSX.utils.json_to_sheet(Array.isArray(data) ? data : [data]); const workbook = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(workbook, worksheet, sheetNameArray[0]); @@ -51,10 +78,35 @@ export const downloadExcelFormat = ({ data, fileName = new Date().getTime().toSt } } }; + const applyRangeFormat = (rangeStr: string | undefined, decimal: number) => { + if (!rangeStr) return; + const rangeInfo = XLSX.utils.decode_range(rangeStr); + for (let row = rangeInfo.s.r; row <= rangeInfo.e.r; row++) { + for (let col = rangeInfo.s.c; col <= rangeInfo.e.c; col++) { + const cellAddr = XLSX.utils.encode_cell({ r: row, c: col }); + const cell = worksheet[cellAddr]; + if (cell && typeof cell.v === "number") { + if (decimal === 1) { + cell.z = "#,##0.0"; + } else if (decimal === 2) { + cell.z = "#,##0.00"; + } else if (decimal === 3) { + cell.z = "#,##0.000"; + } else { + cell.z = "#,##0"; + } + } + } + } + }; chaneValue(decimalArray0, 0); chaneValue(decimalArray1, 1); chaneValue(decimalArray2, 2); chaneValue(decimalArray3, 3); + applyRangeFormat(decimalRange0, 0); + applyRangeFormat(decimalRange1, 1); + applyRangeFormat(decimalRange2, 2); + applyRangeFormat(decimalRange3, 3); excelStyle(XLSX.write(workbook, { bookType: "xlsx", type: "buffer" }), headerRange, fileName); }; @@ -64,6 +116,10 @@ export type SheetParameter = { headerRange: string; sheetName: string; startCol?: string; + decimalRange0?: string; + decimalRange1?: string; + decimalRange2?: string; + decimalRange3?: string; }; export const downloadMultipleExcel = (fileName = new Date().getTime().toString() + '.xlsx', sheetArray: SheetParameter[]): Promise<JSON | undefined> => { @@ -191,9 +247,29 @@ export const filePickedBlob=(oFile: ArrayBuffer, data:[][],fileNm:string, header }); } -export const filePickedBlobArray = (oFile: ArrayBuffer, fileNm: string, sheetArray: SheetParameter[]): Promise<JSON | undefined> => { - return new Promise<JSON | undefined>(async () => { - const wb = XLSX.read(oFile, { type: 'binary' }); +// export const filePickedBlobArray = (oFile: ArrayBuffer, fileNm: string, sheetArray: SheetParameter[]): Promise<JSON | undefined> => { +// return new Promise<JSON | undefined>(async () => { +// const wb = XLSX.read(oFile, { type: 'binary' }); +// wb.SheetNames.forEach(function (sheetName, index) { +// if (sheetArray[index]) { +// const sheet = wb.Sheets[sheetName]; +// if (sheet) { +// const startCol = sheetArray[index].startCol ? sheetArray[index].startCol : 'A'; +// XLSX.utils.sheet_add_aoa(sheet, sheetArray[index].data, { origin: startCol + sheetArray[index].headerNum }); +// } else { +// console.error(`Sheet "${sheetName}" is undefined.`); +// } +// } +// }); +// await excelArrayStyle(XLSX.write(wb, { bookType: "xlsx", type: "buffer" }), sheetArray, fileNm) +// }); +// } +export const filePickedBlobArray = async ( + oFile: ArrayBuffer, + fileNm: string, + sheetArray: SheetParameter[] +): Promise<JSON | undefined> => { + const wb = XLSX.read(oFile, { type: 'binary' }); wb.SheetNames.forEach(function (sheetName, index) { if (sheetArray[index]) { const sheet = wb.Sheets[sheetName]; @@ -205,9 +281,68 @@ export const filePickedBlobArray = (oFile: ArrayBuffer, fileNm: string, sheetArr } } }); - await excelArrayStyle(XLSX.write(wb, { bookType: "xlsx", type: "buffer" }), sheetArray, fileNm) + await excelArrayStyle(XLSX.write(wb, { bookType: "xlsx", type: "buffer" }), sheetArray, fileNm); + return undefined; +}; + +export const filePickedBlobArrayFormat = async ( + oFile: ArrayBuffer, + fileNm: string, + sheetArray: SheetParameter[] +): Promise<JSON | undefined> => { + const wb = XLSX.read(oFile, { type: "binary" }); + + wb.SheetNames.forEach((sheetName, index) => { + if (sheetArray[index]) { + const sheet = wb.Sheets[sheetName]; + if (sheet) { + const startCol = sheetArray[index].startCol ?? "A"; + + XLSX.utils.sheet_add_aoa(sheet, sheetArray[index].data, { + origin: startCol + sheetArray[index].headerNum, + }); + const applyRangeFormat = (rangeStr: string | undefined, decimal: number) => { + if (!rangeStr) return; + const parts = rangeStr.split(",").map((part) => part.trim()).filter(Boolean); + for (const part of parts) { + const rangeInfo = XLSX.utils.decode_range(part); + for (let row = rangeInfo.s.r; row <= rangeInfo.e.r; row++) { + for (let col = rangeInfo.s.c; col <= rangeInfo.e.c; col++) { + const cellAddr = XLSX.utils.encode_cell({ r: row, c: col }); + const cell = sheet[cellAddr]; + if (cell && typeof cell.v === "number") { + if (decimal === 1) { + cell.z = "#,##0.0"; + } else if (decimal === 2) { + cell.z = "#,##0.00"; + } else if (decimal === 3) { + cell.z = "#,##0.000"; + } else { + cell.z = "#,##0"; + } + } + } + } + } + }; + applyRangeFormat(sheetArray[index].decimalRange0, 0); + applyRangeFormat(sheetArray[index].decimalRange1, 1); + applyRangeFormat(sheetArray[index].decimalRange2, 2); + applyRangeFormat(sheetArray[index].decimalRange3, 3); + } else { + console.error(`Sheet "${sheetName}" is undefined.`); + } + } }); -} + + await excelArrayStyle( + XLSX.write(wb, { bookType: "xlsx", type: "buffer" }), + sheetArray, + fileNm + ); + + return undefined; +}; type ExcelDownloadWithHeaderProps = { data: JSON | JSON[]; @@ -241,4 +376,4 @@ export const downloadExcelWithKoreanHeaders = async ({ // 3. 헤더만 한글로 교체하는 커스텀 스타일 함수 호출 await excelStyleWithKoreanHeaders(buffer, headerRange, fileName, headerMapping, dataArray[0]); -}; \ No newline at end of file +}; diff --git a/packages/common/business_components/src/ui/file-upload/lib/file_upload.tsx b/packages/common/business_components/src/ui/file-upload/lib/file_upload.tsx index 42662ab1d..030ddda95 100644 --- a/packages/common/business_components/src/ui/file-upload/lib/file_upload.tsx +++ b/packages/common/business_components/src/ui/file-upload/lib/file_upload.tsx @@ -93,6 +93,7 @@ export const FileUploader = forwardRef< ".jpeg": "image/jpeg", ".png": "image/png", ".gif": "image/gif", + ".bmp": "image/bmp", ".doc": "application/msword", ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document", ".xls": "application/vnd.ms-excel", diff --git a/packages/common/business_components/src/ui/index.ts b/packages/common/business_components/src/ui/index.ts index db1132b58..ae419bd83 100644 --- a/packages/common/business_components/src/ui/index.ts +++ b/packages/common/business_components/src/ui/index.ts @@ -15,6 +15,7 @@ export * from "./search_form_right"; export * from "./search_input"; export * from "./search_input_select"; // export * from "./search_input_address"; +export * from "./search_address"; export * from "./select"; export * from "./tab"; export * from "./table"; @@ -24,4 +25,4 @@ export * from "./sdmInsertForm"; export * from "./popover_ui"; export * from "./date_month_picker"; export * from "./date_picker_range" -export * from "./map/GisOverlay"; \ No newline at end of file +export * from "./map/GisOverlay"; diff --git a/packages/common/business_components/src/ui/map/lib/gis.core.js b/packages/common/business_components/src/ui/map/lib/gis.core.js index e8e05f037..2e97e8af5 100644 --- a/packages/common/business_components/src/ui/map/lib/gis.core.js +++ b/packages/common/business_components/src/ui/map/lib/gis.core.js @@ -172,7 +172,7 @@ GisApp.core.prototype = { wmtsSource.setTileUrlFunction((tileCoord) => { const [z, x, y] = tileCoord; const matrix = matrixIds[z]; - console.log(GisApp.NGII_WMTS_URL.replace("{z}", matrix).replace("{x}", x).replace("{y}", y)); + // console.log(GisApp.NGII_WMTS_URL.replace("{z}", matrix).replace("{x}", x).replace("{y}", y)); return GisApp.NGII_WMTS_URL .replace("{z}", matrix) .replace("{x}", x) diff --git a/packages/common/business_components/src/ui/sdmInsertForm/form.tsx b/packages/common/business_components/src/ui/sdmInsertForm/form.tsx index 6f0cbff2b..b2499ab43 100644 --- a/packages/common/business_components/src/ui/sdmInsertForm/form.tsx +++ b/packages/common/business_components/src/ui/sdmInsertForm/form.tsx @@ -1,4 +1,4 @@ -import { Button, Input, Label } from "@common/components"; +import { Button, Input } from "@common/components"; import { AsYouType } from "libphonenumber-js"; import React, { useCallback, useEffect, useState } from "react"; import type { ControllerRenderProps, UseFormReturn } from "react-hook-form"; @@ -6,10 +6,7 @@ import { FormField } from "../form"; import { FormUi } from "../form_ui"; import { SelectBox } from "../select"; import { SdmSearchForm } from "./sdmNameBox"; -import { UiModal, UiTable } from '../../ui'; -import type { Zip, ZipSearchParams } from "../search_input_address/zipService"; -import { findAddress } from "../search_input_address/findAddress"; -import { cn } from "@common/components"; +import { SearchAddressModal } from "../search_address/SearchAddressModal"; // 수요처 정보 export type DemandCompType = { @@ -91,7 +88,6 @@ export function SdmRegForm({ readOnly, openNetwork, }: SdmRegFormProps) { - const PAGE_SIZE = 10; const [sidoValue, setSidoValue] = useState<string>(""); const [sigunValue, setSigunValue] = useState<string>(""); const [filteredSigunData, setFilteredSigunData] = useState<SelectDataType[]>([ @@ -104,30 +100,6 @@ export function SdmRegForm({ const [zipId, setZipId] = useState<number | undefined>(undefined); const [address, setAddress] = useState<string | undefined>(""); const [detailedAddress, setDetailedAddress] = useState<string>(""); - const [curPage, setCurPage] = useState<number>(1); - // 우편번호 검색 관련 상태 - const [zipSearchResults, setZipSearchResults] = useState<Zip[]>([]); - const [zipTotalElements, setZipTotalElements] = useState<number>(-1); - const [searchQuery, setSearchQuery] = useState<ZipSearchParams>({ - roadNm: "", - bldgNoMno: null, - bldgNoSno: null, - page: curPage, - size: PAGE_SIZE - }); - const [isResetPage, setIsResetPage] = useState<boolean>(false); - const [isResetSelection, setIsResetSelection] = useState<boolean>(false); - const [selectedRowIndex, setSelectedRowIndex] = useState<number | undefined>(undefined); - const [selectedZip, setSelectedZip] = useState<Zip>(); - const [isSelectedZip, setIsSelectedZip] = useState<boolean>(false); - - const handleCellClick = (index: any, headId: any) => { - console.log('index', index); - console.log('headId', headId); - setSelectedZip(zipSearchResults[index]); - setIsSelectedZip(true); - console.log('selectedZip', zipSearchResults[index]); - }; const formatPhoneNumber = (value: string) => { const input = value; @@ -293,17 +265,6 @@ export function SdmRegForm({ [orgBznmNo, setIsDuplicate], ); - // 우편번호 선택 함수 - const handleSelectAddress = () => { - setZipId(selectedZip?.zipId); - setAddress(selectedZip?.zipAddr || ""); - setDetailedAddress(""); - setIsZipModalOpen(false); - setSelectedZip(undefined); - setIsSelectedZip(false); - setSelectedRowIndex(undefined); - }; - useEffect(() => { if (updateList?.dmdInfoId) { form.reset(updateList); @@ -325,16 +286,6 @@ export function SdmRegForm({ console.log("form.formState.errors ", form.formState.errors); }, [form.formState.errors]); - useEffect(() => { - console.log("curPage ", curPage); - searchQuery.page = curPage - 1; - const fetchAddress = async () => { - const result = await findAddress(searchQuery); - setZipSearchResults(result?.content); - }; - fetchAddress(); - }, [curPage]); - // 폼 제출 전에 주소 합치기 useEffect(() => { // address와 detailedAddress를 합쳐서 daddr에 설정 @@ -344,105 +295,6 @@ export function SdmRegForm({ } }, [address, detailedAddress, form]); - const renderBody = () => ( - <> - <form - onSubmit={async (e) => { - e.preventDefault(); - e.stopPropagation(); - const result = await findAddress(searchQuery); - setZipSearchResults(result?.content); - setZipTotalElements(result?.totalElements); - }} - className="mb-4 space-y-2" - > - {/* 검색 조건 부분 */} - <div className="card card-border !m-0 flex flex-col gap-4 p-4"> - <div className="grid grid-cols-3 gap-4"> - <div className="flex flex-col"> - <Label className="mb-1 font-bold text-base">도로명</Label> - <Input - value={searchQuery.roadNm || ""} - onChange={(e) => - setSearchQuery({ - ...searchQuery, - roadNm: e.target.value, - }) - } - placeholder="예: 테헤란로" - className="!text-[1.3rem]" - /> - </div> - - <div className="flex flex-col"> - <Label className="mb-1 text-base">건물본번</Label> - <Input - type="number" - value={searchQuery.bldgNoMno ?? ""} - onChange={(e) => { - const value = e.target.value - ? parseInt(e.target.value) - : null; - setSearchQuery({ - ...searchQuery, - bldgNoMno: value, - }); - }} - placeholder="예: 123" - className="!text-[1.3rem]" - /> - </div> - - <div className="flex flex-col"> - <Label className="mb-1 text-base">건물부번</Label> - <Input - type="number" - value={searchQuery.bldgNoSno ?? ""} - onChange={(e) => { - const value = e.target.value - ? parseInt(e.target.value) - : null; - setSearchQuery({ - ...searchQuery, - bldgNoSno: value, - }); - }} - placeholder="예: 45" - className="!text-[1.3rem]" - /> - </div> - </div> - - <div className="flex justify-end"> - <Button className="rounded-md" type="submit">검색</Button> - </div> - </div> - </form> - - {/* 검색 결과 부분 */} - {zipTotalElements > 0 && ( - <UiTable - headName='' - tableData={zipSearchResults} - headlist={[{ id: 'zoneNo', title: '우편번호' }, { id: 'zipAddr', title: '주소' }]} - pageSize={PAGE_SIZE} - total={zipTotalElements} - cellClick={handleCellClick} - onPageChange={setCurPage} - resetPageNo={isResetPage} - resetSelection={isResetSelection} - index={selectedRowIndex} - /> - )} - {zipTotalElements === 0 && ( - <div className={cn("flex h-96 flex-col items-center justify-center gap-4")}> - <i className="diveicon di-info-circle text-3xl" /> - 조회된 데이터가 없습니다. - </div> - )} - </> - ); - const formFields: Array<{ name: keyof FormSchema; @@ -725,35 +577,14 @@ export function SdmRegForm({ /> ); })} - <UiModal - headerTitle="주소 검색" - headerTitleClass="text-2xl font-bold text-gray-900" + <SearchAddressModal open={isZipModalOpen} onOpenChange={setIsZipModalOpen} - bodyClass={` - ${zipTotalElements > -1 ? 'h-[80vh]' : 'h-[30vh]'} - w-[30vw] - min-w-[500px] - max-w-[90vw] - max-h-[85vh] - overflow-y-auto - !fixed - !top-[50%] - !left-[50%] - !-translate-x-1/2 - !-translate-y-1/2 - z-[101] - flex - flex-col - `} - body={renderBody()} - footerContent={ - <div className="w-full flex justify-center items-center gap-4"> - <Button className="rounded-md" size="lg" onClick={handleSelectAddress} disabled={!isSelectedZip}>선택</Button> - <Button className="rounded-md" size="lg" onClick={() => setIsZipModalOpen(false)}>취소</Button> - </div> - } - useDirectRender={true} + onSelect={(zip) => { + setZipId(zip.zipId); + setAddress(zip.zipAddr || ""); + setDetailedAddress(""); + }} /> </> ); diff --git a/packages/common/business_components/src/ui/search_address/SearchAddressModal.tsx b/packages/common/business_components/src/ui/search_address/SearchAddressModal.tsx new file mode 100644 index 000000000..699d3cc50 --- /dev/null +++ b/packages/common/business_components/src/ui/search_address/SearchAddressModal.tsx @@ -0,0 +1,221 @@ +import { Button, Input, Label, cn } from "@common/components"; +import React, { useState, useEffect } from "react"; +import { UiModal } from "../dialog_ui"; +import { UiTable } from "../table"; +import type { Zip, ZipSearchParams } from "./zipService"; +import { findAddress } from "./findAddress"; + +interface SearchAddressModalProps { + open: boolean; + onOpenChange: (open: boolean) => void; + onSelect: (zip: Zip) => void; +} + +export function SearchAddressModal({ open, onOpenChange, onSelect }: SearchAddressModalProps) { + const PAGE_SIZE = 10; + const [curPage, setCurPage] = useState<number>(1); + const [zipSearchResults, setZipSearchResults] = useState<Zip[]>([]); + const [zipTotalElements, setZipTotalElements] = useState<number>(-1); + const [searchQuery, setSearchQuery] = useState<ZipSearchParams>({ + roadNm: "", + bldgNoMno: null, + bldgNoSno: null, + page: 1, + size: PAGE_SIZE + }); + const [isResetPage, setIsResetPage] = useState<boolean>(false); + const [isResetSelection] = useState<boolean>(false); + const [selectedRowIndex, setSelectedRowIndex] = useState<number | undefined>(undefined); + const [selectedZip, setSelectedZip] = useState<Zip>(); + const [isSelectedZip, setIsSelectedZip] = useState<boolean>(false); + + // Initialize/Reset state when modal opens + useEffect(() => { + if (open) { + setCurPage(1); + setZipSearchResults([]); + setZipTotalElements(-1); + setSearchQuery({ + roadNm: "", + bldgNoMno: null, + bldgNoSno: null, + page: 0, + size: PAGE_SIZE + }); + setSelectedZip(undefined); + setIsSelectedZip(false); + setSelectedRowIndex(undefined); + setIsResetPage(true); + setTimeout(() => setIsResetPage(false), 100); + } + }, [open]); + + // Pagination Effect + useEffect(() => { + if (!open || zipTotalElements === -1) return; + + // Correctly update query for pagination without mutation + const nextPage = curPage - 1; + if (searchQuery.page !== nextPage) { + const newQuery = { ...searchQuery, page: nextPage }; + setSearchQuery(newQuery); + + const fetchAddress = async () => { + const result = await findAddress(newQuery); + setZipSearchResults(result?.content || []); + }; + fetchAddress(); + } + }, [curPage, open, zipTotalElements]); + + const handleCellClick = (index: any) => { + setSelectedZip(zipSearchResults[index]); + setIsSelectedZip(true); + setSelectedRowIndex(index); + }; + + const handleSearch = async (e: React.FormEvent) => { + e.preventDefault(); + e.stopPropagation(); + + const query = { ...searchQuery, page: 0 }; + setSearchQuery(query); + setCurPage(1); + setIsResetPage(true); + + const result = await findAddress(query); + setZipSearchResults(result?.content || []); + setZipTotalElements(result?.totalElements || 0); + + setTimeout(() => setIsResetPage(false), 100); + }; + + const handleConfirm = () => { + if (selectedZip) { + onSelect(selectedZip); + onOpenChange(false); + } + }; + + return ( + <UiModal + headerTitle="주소 검색" + headerTitleClass="text-2xl font-bold text-gray-900" + open={open} + onOpenChange={onOpenChange} + bodyClass={` + ${zipTotalElements > -1 ? 'h-[80vh]' : 'h-[30vh]'} + w-[30vw] + min-w-[500px] + max-w-[90vw] + min-h-[235px] + overflow-y-auto + !fixed + !top-[50%] + !left-[50%] + !-translate-x-1/2 + !-translate-y-1/2 + z-[101] + flex + flex-col + `} + body={ + <> + <form + onSubmit={handleSearch} + className="mb-4 space-y-2" + > + <div className="card card-border !m-0 flex flex-col gap-4 p-4"> + <div className="grid grid-cols-3 gap-4"> + <div className="flex flex-col"> + <Label className="mb-1 font-bold text-base">도로명</Label> + <Input + value={searchQuery.roadNm || ""} + onChange={(e) => + setSearchQuery({ + ...searchQuery, + roadNm: e.target.value, + }) + } + placeholder="예: 테헤란로" + className="!text-[1.3rem]" + /> + </div> + + <div className="flex flex-col"> + <Label className="mb-1 text-base">건물본번</Label> + <Input + type="number" + value={searchQuery.bldgNoMno ?? ""} + onChange={(e) => { + const value = e.target.value + ? parseInt(e.target.value) + : null; + setSearchQuery({ + ...searchQuery, + bldgNoMno: value, + }); + }} + placeholder="예: 123" + className="!text-[1.3rem]" + /> + </div> + + <div className="flex flex-col"> + <Label className="mb-1 text-base">건물부번</Label> + <Input + type="number" + value={searchQuery.bldgNoSno ?? ""} + onChange={(e) => { + const value = e.target.value + ? parseInt(e.target.value) + : null; + setSearchQuery({ + ...searchQuery, + bldgNoSno: value, + }); + }} + placeholder="예: 45" + className="!text-[1.3rem]" + /> + </div> + </div> + + <div className="flex justify-end"> + <Button className="rounded-md" type="submit">검색</Button> + </div> + </div> + </form> + + {zipTotalElements > 0 && ( + <UiTable + headName='' + tableData={zipSearchResults} + headlist={[{ id: 'zoneNo', title: '우편번호' }, { id: 'zipAddr', title: '주소' }]} + pageSize={PAGE_SIZE} + total={zipTotalElements} + cellClick={handleCellClick} + onPageChange={setCurPage} + resetPageNo={isResetPage} + resetSelection={isResetSelection} + index={selectedRowIndex} + /> + )} + {zipTotalElements === 0 && ( + <div className={cn("flex h-96 flex-col items-center justify-center gap-4")}> + <i className="diveicon di-info-circle text-3xl" /> + 조회된 데이터가 없습니다. + </div> + )} + </> + } + footerContent={ + <div className="w-full flex justify-center items-center gap-4"> + <Button className="rounded-md" size="lg" onClick={handleConfirm} disabled={!isSelectedZip}>선택</Button> + <Button className="rounded-md" size="lg" onClick={() => onOpenChange(false)}>취소</Button> + </div> + } + useDirectRender={true} + /> + ); +} diff --git a/packages/common/business_components/src/ui/search_address/findAddress.ts b/packages/common/business_components/src/ui/search_address/findAddress.ts new file mode 100644 index 000000000..5e5ba4360 --- /dev/null +++ b/packages/common/business_components/src/ui/search_address/findAddress.ts @@ -0,0 +1,118 @@ + +// const Key = 'U01TX0FVVEgyMDI1MDgyODE0MTAxNzExNjEyMjA' +// const addresssKey = 'devU01TX0FVVEgyMDI1MDgyNTEwMzkwNDExNjEwNTM='; //인증키 만료일은 2025-11-23 + +import type { Zip, ZipSearchParams } from "./zipService"; + +/** + * 주소 API 응답 타입 + * https://business.juso.go.kr/addrlink/main.do 해당 사이트 참조 + */ +export type AddressApiResponse = { + results: { + common: { + errorMessage: string; + countPerPage: string; + totalCount: string; + errorCode: string; + currentPage: string; + }; + juso: addressType[]; + }; +} + +export type addressType = { + detBdNmList: string; + engAddr: string; + rn: string; + emdNm: string; + zipNo: string; + roadAddrPart2: string; + emdNo: string; + sggNm: string; + jibunAddr: string; + siNm: string; + roadAddrPart1: string; + bdNm: string; + admCd: string; + udrtYn: string; + lnbrMnnm: string; + roadAddr: string; + lnbrSlno: string; + buldMnnm: string; + bdKdcd: string; + liNm: string; + rnMgtSn: string; + mtYn: string; + bdMgtSn: string; + buldSlno: string; +} + +/** + * 주소에 따른 검색 + * @param search : 검색어 + * @returns addressType[] 주소 배열 + */ +export const findAddress = async (searchQuery: ZipSearchParams): Promise<any> => { + if (!searchQuery) return []; + + // 쿼리 파라미터 생성 + const params = new URLSearchParams(); + if (searchQuery.roadNm) params.append('roadNm', searchQuery.roadNm); + if (searchQuery.bldgNoMno != null) params.append('bldgNoMno', searchQuery.bldgNoMno.toString()); + if (searchQuery.bldgNoSno != null) params.append('bldgNoSno', searchQuery.bldgNoSno.toString()); + if (searchQuery.page != null) params.append('page', searchQuery.page.toString()); + if (searchQuery.size != null) params.append('size', searchQuery.size.toString()); + + const url = `/cms-service/api/v1/zips?${params.toString()}`; + try { + const response = await fetch(url); + if (!response.ok) throw new Error('Network response was not ok'); + + const data = await response.json(); + if (data) return data + else return []; + } catch (error) { + console.error('Error fetching address data:', error); + return []; + } + +} + + + +export const searchFilter = (search: string) => { + + + if (search.length > 0) { + //특수문자 제거 + const expText = /[%=><]/; + if (expText.test(search) == true) { + alert("특수문자를 입력 할수 없습니다."); + search = search.split(expText).join(""); + return false; + } + + //특정문자열(sql예약어의 앞뒤공백포함) 제거 + const sqlArray = [ + //sql 예약어 + "OR", "SELECT", "INSERT", "DELETE", "UPDATE", "CREATE", "DROP", "EXEC", + "UNION", "FETCH", "DECLARE", "TRUNCATE" + ] + + let regex; + for (let i = 0; i < sqlArray.length; i++) { + regex = new RegExp(sqlArray[i] as string, "gi"); + + if (regex.test(search)) { + alert("\"" + sqlArray[i] + "\"와(과) 같은 특정문자로 검색할 수 없습니다."); + search = search.replace(regex, ""); + return false; + } + } + } + return true; + + + +} diff --git a/packages/common/business_components/src/ui/search_address/index.ts b/packages/common/business_components/src/ui/search_address/index.ts new file mode 100644 index 000000000..b790bb492 --- /dev/null +++ b/packages/common/business_components/src/ui/search_address/index.ts @@ -0,0 +1,3 @@ +export * from "./SearchAddressModal"; +export * from "./findAddress"; +export * from "./zipService"; diff --git a/packages/common/business_components/src/ui/search_address/zipService.ts b/packages/common/business_components/src/ui/search_address/zipService.ts new file mode 100644 index 000000000..03e4a8ccc --- /dev/null +++ b/packages/common/business_components/src/ui/search_address/zipService.ts @@ -0,0 +1,66 @@ +interface SortConfig { + empty: boolean; + sorted: boolean; + unsorted: boolean; +} + +interface PageableConfig { + sort: SortConfig; + offset: number; + pageNumber: number; + pageSize: number; + paged: boolean; + unpaged: boolean; +} + +export interface PageableList<T> { + content: T[]; + pageable: PageableConfig; + sort: SortConfig; + totalPages: number; + totalElements: number; + number: number; + numberOfElements: number; + size: number; + first: boolean; + last: boolean; + empty: boolean; +} + +export interface PageableParams { + page?: number; + size?: number; + sort?: string; +} + + +export interface ZipSearchParams extends PageableParams { + zoneNo?: string; // 구역번호 + ctpvNm?: string; // 시도명 + sggNm?: string; // 시군구명 + emdNm?: string; // 읍면동명 + roadNm?: string; // 도로명 + stdgNm?: string; // 법정동명 + dongNm?: string; // 행정동명 + bldgNoMno?: number | null; // 빌딩본번 + bldgNoSno?: number | null; // 빌딩딩부번 + lotnoMno?: number | null; // 지번본번 + lotnoSno?: number | null; // 지번부번 +} + +export interface Zip { + zipId: number; + zoneNo: string; + ctpvNm: string; + sggNm: string; + emdNm: string; + roadNm: string; + bldgNoMno: number; + bldgNoSno: number; + lotnoMno: number; + lotnoSno: number; + lotno: string; + zipAddr: string; +} + +export type ZipList = PageableList<Zip>; diff --git a/packages/common/business_components/src/ui/search_base_form/index.tsx b/packages/common/business_components/src/ui/search_base_form/index.tsx index 82798db0b..9cef315c3 100644 --- a/packages/common/business_components/src/ui/search_base_form/index.tsx +++ b/packages/common/business_components/src/ui/search_base_form/index.tsx @@ -26,6 +26,7 @@ type SearchBaseType = { leftSelectClassName?: string; buttonOrder?: ("query" | "init" | "excel")[]; initSidoValue?: string | ''; + excelDisabled?: boolean; } export type ReturnType = { partValue: string; @@ -60,6 +61,7 @@ function SearchBaseForm ({ leftSelectClassName, buttonOrder, initSidoValue='', + excelDisabled=false, }: SearchBaseType) { const [partValue, setPartValue] = useState<string>("A"); const [sidoValue, setSidoValue] = useState<string>(initSidoValue?initSidoValue:"0"); @@ -87,7 +89,7 @@ function SearchBaseForm ({ ); case "excel": return ( - <Button className={btnClass} color="green" onClick={excelDownload} key="excel"> + <Button className={btnClass} color="green" onClick={excelDownload} key="excel" disabled={excelDisabled}> {excelName ? excelName : "엑셀다운로드"} </Button> ); @@ -191,4 +193,4 @@ function SearchBaseForm ({ } export { SearchBaseForm }; - \ No newline at end of file + diff --git a/packages/common/business_components/src/ui/select/index.tsx b/packages/common/business_components/src/ui/select/index.tsx index f9dac92d6..32510eb18 100644 --- a/packages/common/business_components/src/ui/select/index.tsx +++ b/packages/common/business_components/src/ui/select/index.tsx @@ -1,7 +1,8 @@ // selectBox import { Label } from "@common/components"; -import React, { useEffect, useState } from "react"; +// import React, { useEffect, useState } from "react"; +import React, { useState } from "react"; import { Select, SelectContent, @@ -15,6 +16,7 @@ interface SelectBox1 { text: string; val: string | number; hidden?: boolean; + selected?: boolean; } const SelectBox = React.forwardRef< @@ -51,18 +53,22 @@ const SelectBox = React.forwardRef< const [isOpen, setIsOpen] = useState(false); // hidden이 true인 항목만 드롭다운에서 제외 const visibleItems = selectArray.filter((item) => !item.hidden); + const selectedItem = visibleItems.find((item) => item.selected); + + const resolvedValue = + value === undefined && selectedItem ? selectedItem.val : value; // 현재 값이 hidden 항목이거나 빈 값인 경우 undefined로 설정하여 placeholder가 표시되도록 함 const currentValue = - value !== undefined && + resolvedValue !== undefined && hiddenItem && - (String(value) === String(hiddenItem.val) || - value === "" || - value === "0" || - value === 0) + (String(resolvedValue) === String(hiddenItem.val) || + resolvedValue === "" || + resolvedValue === "0" || + resolvedValue === 0) ? undefined - : value !== undefined - ? String(value) + : resolvedValue !== undefined + ? String(resolvedValue) : undefined; return ( diff --git a/packages/common/business_components/src/ui/table/index.tsx b/packages/common/business_components/src/ui/table/index.tsx index dc763d00b..d2fd81de6 100644 --- a/packages/common/business_components/src/ui/table/index.tsx +++ b/packages/common/business_components/src/ui/table/index.tsx @@ -16,14 +16,16 @@ export type ChkClass = { chk: boolean; readonly: boolean; }; -type UiTableProps = { +type UiTableProps<T extends object> = { headName: React.ReactNode | undefined; - tableData: { [key: string]: any }[]; + tableData: T[]; headlist: UiHeadProps[]; pageSize: number; total: number; cellClick: ((index: number | undefined, headId: string) => void) | undefined; onPageChange: (page: number) => void; + cellClassName?: (row: T, headId: string, rowIndex: number) => string; + rowClassName?: (row: T, rowIndex: number) => string; className?: string; resetPageNo?: boolean; children?: React.ReactNode | undefined; @@ -43,9 +45,10 @@ type UiHeadProps = { row?: number; widthPercent?: number; rowSpan?: number; + colSpan?: number; }; -export function UiTable({ +export function UiTable<T extends object = Record<string, unknown>>({ tableData, headlist, pageSize, @@ -54,6 +57,8 @@ export function UiTable({ children, cellClick, onPageChange, + cellClassName, + rowClassName, className = "", resetPageNo = false, boldArray = [], @@ -62,7 +67,7 @@ export function UiTable({ curPage = 1, headButton = undefined, widthFixed = false -}: UiTableProps) { +}: UiTableProps<T>) { const [cur, setCur] = useState<number>(curPage); const [idx, setIdx] = useState<number>(index); const [headerHeight, setHeaderHeight] = useState<number>(0); @@ -118,7 +123,6 @@ export function UiTable({ }; const leafCols = getLeafCols(headlist); - return ( <> {headName ? ( @@ -136,20 +140,20 @@ export function UiTable({ <></> )} <div className={`card !p-0r !m-[1.2rem] ${className}`}> - <Table variant="secondary" className={widthFixed?"table-fixed":""}> - {widthFixed? - <colgroup> - {leafCols.map(col => ( - <col key={col.id} style={{ width: col.widthPercent ? `${col.widthPercent}%` : undefined }} /> - ))} - </colgroup>:""} + <Table variant="secondary" className={widthFixed ? "table-fixed" : ""}> + {widthFixed ? + <colgroup> + {leafCols.map(col => ( + <col key={col.id} style={{ width: col.widthPercent ? `${col.widthPercent}%` : undefined }} /> + ))} + </colgroup> : ""} <TableHeader ref={headerRef} className="font-bold"> {children ? children : <HeadMakeColSpan upHeadList={headlist} />} </TableHeader> <TableBody className="text-center"> {tableData.length > 0 ? ( - tableData?.map((el: Record<string, unknown>, index) => ( - <TableRow key={`row-${index}`}> + tableData?.map((el: T, index) => ( + <TableRow key={`row-${index}`} className={rowClassName ? rowClassName(el, index) : ""}> {headlist .filter((item) => item.visible != false) .map( @@ -163,29 +167,32 @@ export function UiTable({ : cellClick ? "cursor-pointer" : "" - } sticky top-20 z-40 bg-secondary !font-bold whitespace-pre-line`} + } ${cellClassName ? cellClassName(el, head.id, index) : ""} sticky top-20 z-40 bg-secondary !font-bold whitespace-pre-line`} style={{ top: `${headerHeight + index * 32 - 4}px` }} onClick={() => handleCellClick(index, head.id)} key={`cell-${head.id}-${index}`} > - {typeof el[head.id] !== "number" - ? String(el[head.id] ?? "") - : String(el[head.id]).includes(".") - ? String(el[head.id]) - .split(".") - .map((part, index) => - index === 0 - ? part.replace( - /\B(?=(\d{3})+(?!\d))/g, - ",", - ) - : part, - ) - .join(".") - : String(el[head.id] ?? "").replace( - /\B(?=(\d{3})+(?!\d))/g, - ",", - )} + {(() => { + const cellValue = (el as Record<string, unknown>)[head.id]; + return typeof cellValue !== "number" + ? String(cellValue ?? "") + : String(cellValue).includes(".") + ? String(cellValue) + .split(".") + .map((part, index) => + index === 0 + ? part.replace( + /\B(?=(\d{3})+(?!\d))/g, + ",", + ) + : part, + ) + .join(".") + : String(cellValue ?? "").replace( + /\B(?=(\d{3})+(?!\d))/g, + ",", + ); + })()} </TableCell> ) : ( <TableCell @@ -194,47 +201,49 @@ export function UiTable({ : cellClick ? "cursor-pointer" : "" - } mt-[${boldArray.length * 40}px] whitespace-pre-line`} + } ${cellClassName ? cellClassName(el, head.id, index) : ""} mt-[${boldArray.length * 40}px] whitespace-pre-line`} onClick={() => handleCellClick(index, head.id)} key={`cell-${head.id}-${index}`} > { //체크박스 유형애서 readOnly가 필요한 경우를 위해서 ChkClass 를 사용해서 readOnly 처리함. - typeof el[head.id] === "object" && - el[head.id] !== null && - "chk" in (el[head.id] as object) && - "readonly" in (el[head.id] as object) ? ( + (() => { + const cellValue = (el as Record<string, unknown>)[head.id]; + return typeof cellValue === "object" && + cellValue !== null && + "chk" in (cellValue as object) && + "readonly" in (cellValue as object) ? ( <Checkbox checked={ - (el[head.id] as ChkClass)?.chk === true + (cellValue as ChkClass)?.chk === true } onCheckedChange={(checked) => { { - console.log(el[head.id]); + console.log(cellValue); } if ( - (el[head.id] as ChkClass).readonly == true + (cellValue as ChkClass).readonly == true ) return; - (el[head.id] as ChkClass).chk = + (cellValue as ChkClass).chk = Boolean(checked); }} > <Check className="h-6 w-6 text-primary" /> </Checkbox> - ) : typeof el[head.id] === "boolean" ? ( + ) : typeof cellValue === "boolean" ? ( <Checkbox - checked={el[head.id] === true} + checked={cellValue === true} onCheckedChange={(checked) => { - el[head.id] = checked; + (el as Record<string, unknown>)[head.id] = checked; }} > <Check className="h-6 w-6 text-primary" /> </Checkbox> - ) : typeof el[head.id] !== "number" ? ( - String(el[head.id] ?? "") - ) : String(el[head.id]).includes(".") ? ( - String(el[head.id]) + ) : typeof cellValue !== "number" ? ( + String(cellValue ?? "") + ) : String(cellValue).includes(".") ? ( + String(cellValue) .split(".") .map((part, index) => index === 0 @@ -246,11 +255,12 @@ export function UiTable({ ) .join(".") ) : ( - String(el[head.id] ?? "").replace( + String(cellValue ?? "").replace( /\B(?=(\d{3})+(?!\d))/g, ",", ) - ) + ); + })() } </TableCell> )), @@ -261,7 +271,12 @@ export function UiTable({ <TableRow> <TableCell colSpan={ - headlist.filter((item) => item.visible != false).length + headlist.filter((item) => item.visible != false).length - + headlist.filter( + (item) => + item.visible != false && + (item.colSpan || ["A", "B", "C"].includes(item.id)), + ).length } className="h-24 text-center text-gray-500" > diff --git a/packages/common/components/src/api/menu.ts b/packages/common/components/src/api/menu.ts index ce6465b84..b4d84c95e 100644 --- a/packages/common/components/src/api/menu.ts +++ b/packages/common/components/src/api/menu.ts @@ -49,7 +49,7 @@ export const getAnonymouseMenuList = async ( siteId: string, ): Promise<MenuItem[]> => { const response = await fetch( - `${BASE_URL}/${ENDPOINT.CMS_SERVICE.MENU_ANONYMOUS}/user/${siteId}`, + `${BASE_URL}${ENDPOINT.CMS_SERVICE.MENU_ANONYMOUS}/user/${siteId}`, { method: "GET", headers: { diff --git a/packages/common/components/src/ui/header/index.tsx b/packages/common/components/src/ui/header/index.tsx index 97ed4c151..b9f7fa628 100644 --- a/packages/common/components/src/ui/header/index.tsx +++ b/packages/common/components/src/ui/header/index.tsx @@ -64,7 +64,99 @@ export function Header({ viewOnly }: { viewOnly?: boolean }) { queryFn: () => getSiteList(), enabled: isLoggedIn && !viewOnly, }); - + const logout = async () => { + try { + // 1. 백엔드 API 로그아웃 호출 + const response = await fetch("/api/auth/logout", { + method: "POST", + credentials: "include", // ⭐ 쿠키 포함 중요! + }); + + if (!response.ok) { + console.error("Logout API failed"); + // API 실패해도 계속 진행 + } + } catch (error) { + console.error("Logout error:", error); + // 에러가 나도 계속 진행 + } finally { + // 2. 클라이언트 쿠키 삭제 (백업) + clearClientCookies(); + + // 3. 로컬/세션 스토리지 정리 + if (typeof window !== "undefined") { + localStorage.clear(); + sessionStorage.clear(); + } + + // 4. 로그인 페이지로 리다이렉트 + window.location.href = "/auth/login"; + } + }; + + // 쿠키 삭제 헬퍼 함수 + const clearClientCookies = () => { + const actualHost = window.location.hostname; + + // 루트 도메인 추출 + const getRootDomain = (hostname: string): string => { + if (hostname === "localhost" || /^\d+\.\d+\.\d+\.\d+$/.test(hostname)) { + return hostname; + } + + if (hostname.includes("hasuon.or.kr")) { + return ".hasuon.or.kr"; + } + + const parts = hostname.split("."); + if (parts.length >= 2) { + return `.${parts.slice(-2).join(".")}`; + } + + return hostname; + }; + + const domain = getRootDomain(actualHost); + + // 삭제할 쿠키 목록 + const cookiesToDelete = [ + appConfig.AUTH_USER_ID, + appConfig.REFRESH_TOKEN, + appConfig.ACCESS_TOKEN, + appConfig.CLAIM_NAME, + "site-id-key", + "site-theme-key", + ].filter(Boolean); // undefined 제거 + + // 여러 방법으로 쿠키 삭제 시도 + cookiesToDelete.forEach((cookieName) => { + if (!cookieName) return; + + // 방법 1: removeCookie 함수 사용 + if (domain === "localhost") { + removeCookie(cookieName, {}); + } else { + removeCookie(cookieName, { domain }); + } + + // 방법 2: 직접 document.cookie 설정 (도메인 포함) + if (domain !== "localhost") { + document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; domain=${domain}`; + } + + // 방법 3: 도메인 없이 삭제 (현재 도메인만) + document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`; + + // 방법 4: Secure 속성 포함 삭제 + if (window.location.protocol === "https:") { + if (domain !== "localhost") { + document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; domain=${domain}; secure`; + } + document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; secure`; + } + }); + }; + /* const logout = async () => { try { const response = await fetch("/api/auth/logout", { @@ -72,29 +164,58 @@ export function Header({ viewOnly }: { viewOnly?: boolean }) { }); if (!response.ok) throw new Error("Failed to logout"); - // 쿠키 삭제 + const actualHost = window.location.hostname; + + // 서브도메인 쿠키 삭제를 위한 루트 도메인 추출 + const getRootDomain = (hostname: string): string => { + // localhost나 IP 주소인 경우 + if (hostname === "localhost" || /^\d+\.\d+\.\d+\.\d+$/.test(hostname)) { + return hostname; + } + + // hasuon.or.kr 도메인인 경우 + if (hostname.includes("hasuon.or.kr")) { + return ".hasuon.or.kr"; + } + + // 일반적인 도메인의 경우 루트 도메인 추출 (예: sub.example.com → .example.com) + const parts = hostname.split("."); + if (parts.length >= 2) { + // 마지막 두 부분을 사용 (예: example.com) + return `.${parts.slice(-2).join(".")}`; + } + + return hostname; + }; + + const domain = getRootDomain(actualHost); + const cookieOptions = domain.startsWith(".") || domain === "localhost" + ? { domain } + : {}; + + // 쿠키 삭제 (서브도메인 포함) if (appConfig.AUTH_USER_ID) { - removeCookie(appConfig.AUTH_USER_ID); + removeCookie(appConfig.AUTH_USER_ID, cookieOptions); } if (appConfig.REFRESH_TOKEN) { - removeCookie(appConfig.REFRESH_TOKEN); + removeCookie(appConfig.REFRESH_TOKEN, cookieOptions); } if (appConfig.ACCESS_TOKEN) { - removeCookie(appConfig.ACCESS_TOKEN); + removeCookie(appConfig.ACCESS_TOKEN, cookieOptions); } if (appConfig.CLAIM_NAME) { - removeCookie(appConfig.CLAIM_NAME); + removeCookie(appConfig.CLAIM_NAME, cookieOptions); } - removeCookie("site-id-key"); - removeCookie("site-theme-key"); + removeCookie("site-id-key", cookieOptions); + removeCookie("site-theme-key", cookieOptions); window.location.href = "/auth/login"; } catch (error) { console.error(error); } }; - +*/ const openPopup = useCallback(async (popup: Popup) => { try { const cookieName = `cs-popup-hide-for-day-${popup.popupSn}`; @@ -153,10 +274,10 @@ export function Header({ viewOnly }: { viewOnly?: boolean }) { <SidebarTrigger className="size-12 !bg-transparent p-2 hover:!bg-accent [&>svg]:!size-full [&>svg]:text-foreground" /> <h1 className="flex flex-col items-center gap-2"> {mounted ? ( - <img + <img ref={logoRef} - src={getLogoImage()} - className="h-[3rem]" + src={getLogoImage()} + className="h-[3rem]" alt="logo" onLoad={() => { if (logoRef.current) { @@ -167,7 +288,7 @@ export function Header({ viewOnly }: { viewOnly?: boolean }) { ) : ( <Skeleton className="h-[3rem] w-[15rem]" /> )} - <span + <span className="text-[1.2rem] font-semibold text-foreground" style={{ letterSpacing: logoWidth > 0 ? `${(logoWidth - 120) / (siteInfo?.siteNm?.length || 1)}px` : '0.05em' diff --git a/packages/common/components/src/ui/header/notice-popover.tsx b/packages/common/components/src/ui/header/notice-popover.tsx index 6ee35371b..251756a94 100644 --- a/packages/common/components/src/ui/header/notice-popover.tsx +++ b/packages/common/components/src/ui/header/notice-popover.tsx @@ -34,6 +34,7 @@ export function NoticePopover() { queryKey: [ENDPOINT.CMS_SERVICE.NOTICE_USER], queryFn: () => getNoticeUserList(userId!), enabled: !!userId, + select: (data) => data.content.filter((item) => item.notReadYn === "N"), }); const { data: unreadNoticeCount } = useQuery({ @@ -53,11 +54,11 @@ export function NoticePopover() { }; const handleSelectAll = () => { - if (noticeUserList?.content) { - if (selectedNoticeIds.length === noticeUserList.content.length) { + if (noticeUserList) { + if (selectedNoticeIds.length === noticeUserList.length) { setSelectedNoticeIds([]); } else { - const allIds = noticeUserList.content.map((item) => item.notiId); + const allIds = noticeUserList.map((item) => item.notiId); setSelectedNoticeIds(allIds); } } @@ -79,9 +80,9 @@ export function NoticePopover() { } }; - const isAllSelected = noticeUserList?.content - ? selectedNoticeIds.length === noticeUserList.content.length && - noticeUserList.content.length > 0 + const isAllSelected = noticeUserList + ? selectedNoticeIds.length === noticeUserList.length && + noticeUserList.length > 0 : false; useEffect(() => { @@ -107,7 +108,7 @@ export function NoticePopover() { </Label> </div> <ul> - {noticeUserList?.content?.map((item) => ( + {noticeUserList?.map((item) => ( <li key={item.notiId} className={cn(