diff --git a/AGENTS.md b/AGENTS.md
deleted file mode 100644
index 1da991844..000000000
--- a/AGENTS.md
+++ /dev/null
@@ -1 +0,0 @@
-[Next.js Docs Index]|root: ./.next-docs|STOP. What you remember about Next.js is WRONG for this project. Always search docs and read before any task.|01-app/01-getting-started:{01-installation.mdx,02-project-structure.mdx,03-layouts-and-pages.mdx,04-images-and-fonts.mdx,05-css.mdx,06-fetching-data.mdx,07-updating-data.mdx,08-error-handling.mdx,10-metadata-and-og-images.mdx,12-upgrading.mdx}|01-app/03-building-your-application/01-routing:{03-layouts-and-templates.mdx,04-linking-and-navigating.mdx,05-error-handling.mdx,06-loading-ui-and-streaming.mdx,07-redirecting.mdx,08-route-groups.mdx,10-dynamic-routes.mdx,11-parallel-routes.mdx,12-intercepting-routes.mdx,13-route-handlers.mdx,14-middleware.mdx,15-internationalization.mdx}|01-app/03-building-your-application/02-data-fetching:{01-fetching.mdx,03-server-actions-and-mutations.mdx,04-incremental-static-regeneration.mdx}|01-app/03-building-your-application/03-rendering:{01-server-components.mdx,02-client-components.mdx,03-composition-patterns.mdx,04-partial-prerendering.mdx,05-edge-and-nodejs-runtimes.mdx}|01-app/03-building-your-application/05-styling:{01-css.mdx,02-tailwind-css.mdx,03-sass.mdx,04-css-in-js.mdx}|01-app/03-building-your-application/06-optimizing:{01-images.mdx,02-videos.mdx,03-fonts.mdx,04-metadata.mdx,05-scripts.mdx,06-package-bundling.mdx,07-lazy-loading.mdx,08-analytics.mdx,09-instrumentation.mdx,10-open-telemetry.mdx,11-static-assets.mdx,12-third-party-libraries.mdx,13-memory-usage.mdx,14-local-development.mdx}|01-app/03-building-your-application/07-configuring:{03-environment-variables.mdx,05-mdx.mdx,06-src-directory.mdx,10-custom-server.mdx,11-draft-mode.mdx,15-content-security-policy.mdx,16-debugging.mdx,16-progressive-web-apps.mdx}|01-app/03-building-your-application/08-testing:{01-vitest.mdx,02-jest.mdx,03-playwright.mdx,04-cypress.mdx}|01-app/03-building-your-application/10-deploying:{01-production-checklist.mdx,02-static-exports.mdx,03-multi-zones.mdx}|01-app/03-building-your-application/11-upgrading:{01-codemods.mdx,02-canary.mdx,03-version-15.mdx,04-version-14.mdx,05-app-router-migration.mdx,06-from-create-react-app.mdx,07-from-vite.mdx,08-single-page-applications.mdx}|01-app/05-api-reference:{07-edge.mdx,08-turbopack.mdx}|01-app/05-api-reference/01-directives:{use-cache.mdx,use-client.mdx,use-server.mdx}|01-app/05-api-reference/02-components:{font.mdx,form.mdx,image.mdx,link.mdx,script.mdx}|01-app/05-api-reference/03-file-conventions/01-metadata:{app-icons.mdx,manifest.mdx,opengraph-image.mdx,robots.mdx,sitemap.mdx}|01-app/05-api-reference/03-file-conventions:{default.mdx,error.mdx,forbidden.mdx,instrumentation-client.mdx,instrumentation.mdx,layout.mdx,loading.mdx,mdx-components.mdx,middleware.mdx,not-found.mdx,page.mdx,route-segment-config.mdx,route.mdx,template.mdx,unauthorized.mdx}|01-app/05-api-reference/04-functions:{after.mdx,cacheLife.mdx,cacheTag.mdx,connection.mdx,cookies.mdx,draft-mode.mdx,fetch.mdx,forbidden.mdx,generate-image-metadata.mdx,generate-metadata.mdx,generate-sitemaps.mdx,generate-static-params.mdx,generate-viewport.mdx,headers.mdx,image-response.mdx,next-request.mdx,next-response.mdx,not-found.mdx,permanentRedirect.mdx,redirect.mdx,revalidatePath.mdx,revalidateTag.mdx,unauthorized.mdx,unstable_cache.mdx,unstable_noStore.mdx,unstable_rethrow.mdx,use-link-status.mdx,use-params.mdx,use-pathname.mdx,use-report-web-vitals.mdx,use-router.mdx,use-search-params.mdx,use-selected-layout-segment.mdx,use-selected-layout-segments.mdx,userAgent.mdx}|01-app/05-api-reference/05-config/01-next-config-js:{allowedDevOrigins.mdx,appDir.mdx,assetPrefix.mdx,authInterrupts.mdx,basePath.mdx,cacheLife.mdx,compress.mdx,crossOrigin.mdx,cssChunking.mdx,devIndicators.mdx,distDir.mdx,dynamicIO.mdx,env.mdx,eslint.mdx,expireTime.mdx,exportPathMap.mdx,generateBuildId.mdx,generateEtags.mdx,headers.mdx,htmlLimitedBots.mdx,httpAgentOptions.mdx,images.mdx,incrementalCacheHandlerPath.mdx,inlineCss.mdx,logging.mdx,mdxRs.mdx,onDemandEntries.mdx,optimizePackageImports.mdx,output.mdx,pageExtensions.mdx,poweredByHeader.mdx,ppr.mdx,productionBrowserSourceMaps.mdx,reactCompiler.mdx,reactMaxHeadersLength.mdx,reactStrictMode.mdx,redirects.mdx,rewrites.mdx,sassOptions.mdx,serverActions.mdx,serverComponentsHmrCache.mdx,serverExternalPackages.mdx,staleTimes.mdx,staticGeneration.mdx,trailingSlash.mdx,transpilePackages.mdx,turbopack.mdx,typedRoutes.mdx,typescript.mdx,urlImports.mdx,useCache.mdx,useLightningcss.mdx,viewTransition.mdx,webVitalsAttribution.mdx,webpack.mdx}|01-app/05-api-reference/05-config:{02-typescript.mdx,03-eslint.mdx}|01-app/05-api-reference/06-cli:{create-next-app.mdx,next.mdx}|02-pages/01-getting-started:{01-installation.mdx,02-project-structure.mdx}|02-pages/02-building-your-application/01-routing:{01-pages-and-layouts.mdx,02-dynamic-routes.mdx,03-linking-and-navigating.mdx,04-redirecting.mdx,05-custom-app.mdx,06-custom-document.mdx,07-api-routes.mdx,08-custom-error.mdx,10-internationalization.mdx,11-middleware.mdx}|02-pages/02-building-your-application/02-rendering:{01-server-side-rendering.mdx,02-static-site-generation.mdx,04-automatic-static-optimization.mdx,05-client-side-rendering.mdx,06-edge-and-nodejs-runtimes.mdx}|02-pages/02-building-your-application/03-data-fetching:{01-get-static-props.mdx,02-get-static-paths.mdx,03-forms-and-mutations.mdx,03-get-server-side-props.mdx,04-incremental-static-regeneration.mdx,05-client-side.mdx}|02-pages/02-building-your-application/04-styling:{01-css.mdx,02-tailwind-css.mdx,03-css-in-js.mdx,04-sass.mdx}|02-pages/02-building-your-application/05-optimizing:{01-images.mdx,02-fonts.mdx,03-scripts.mdx,05-static-assets.mdx,06-package-bundling.mdx,07-analytics.mdx,08-lazy-loading.mdx,09-instrumentation.mdx,10-open-telemetry.mdx,11-third-party-libraries.mdx}|02-pages/02-building-your-application/06-configuring:{03-environment-variables.mdx,05-src-directory.mdx,06-mdx.mdx,07-amp.mdx,08-babel.mdx,09-post-css.mdx,10-custom-server.mdx,11-draft-mode.mdx,12-error-handling.mdx,13-preview-mode.mdx,14-content-security-policy.mdx,16-debugging.mdx}|02-pages/02-building-your-application/07-testing:{01-vitest.mdx,02-jest.mdx,03-playwright.mdx,04-cypress.mdx}|02-pages/02-building-your-application/09-deploying:{01-production-checklist.mdx,02-static-exports.mdx,03-multi-zones.mdx,04-ci-build-caching.mdx}|02-pages/02-building-your-application/10-upgrading:{01-codemods.mdx,02-app-router-migration.mdx,03-from-vite.mdx,04-from-create-react-app.mdx,05-version-14.mdx,06-version-13.mdx,07-version-12.mdx,08-version-11.mdx,09-version-10.mdx,10-version-9.mdx}|02-pages/03-api-reference:{06-edge.mdx,08-turbopack.mdx}|02-pages/03-api-reference/01-components:{font.mdx,form.mdx,head.mdx,image-legacy.mdx,image.mdx,link.mdx,script.mdx}|02-pages/03-api-reference/02-file-conventions:{instrumentation.mdx}|02-pages/03-api-reference/03-functions:{get-initial-props.mdx,get-server-side-props.mdx,get-static-paths.mdx,get-static-props.mdx,next-request.mdx,next-response.mdx,use-amp.mdx,use-report-web-vitals.mdx,use-router.mdx,userAgent.mdx}|02-pages/03-api-reference/04-config/01-next-config-js:{allowedDevOrigins.mdx,assetPrefix.mdx,basePath.mdx,bundlePagesRouterDependencies.mdx,compress.mdx,crossOrigin.mdx,devIndicators.mdx,distDir.mdx,env.mdx,eslint.mdx,exportPathMap.mdx,generateBuildId.mdx,generateEtags.mdx,headers.mdx,httpAgentOptions.mdx,images.mdx,onDemandEntries.mdx,optimizePackageImports.mdx,output.mdx,pageExtensions.mdx,poweredByHeader.mdx,productionBrowserSourceMaps.mdx,reactStrictMode.mdx,redirects.mdx,rewrites.mdx,runtime-configuration.mdx,serverExternalPackages.mdx,trailingSlash.mdx,transpilePackages.mdx,turbo.mdx,typescript.mdx,urlImports.mdx,useLightningcss.mdx,webVitalsAttribution.mdx,webpack.mdx}|02-pages/03-api-reference/04-config:{01-typescript.mdx,02-eslint.mdx}|02-pages/03-api-reference/05-cli:{create-next-app.mdx,next.mdx}|03-architecture:{accessibility.mdx,fast-refresh.mdx,nextjs-compiler.mdx,supported-browsers.mdx}|04-community:{01-contribution-guide.mdx}
diff --git a/apps/cm/src/pages/auth/login/ui/login-page.tsx b/apps/cm/src/pages/auth/login/ui/login-page.tsx
index fed7be4ad..297d28a8f 100644
--- a/apps/cm/src/pages/auth/login/ui/login-page.tsx
+++ b/apps/cm/src/pages/auth/login/ui/login-page.tsx
@@ -145,11 +145,11 @@ function LoginPage() {
- 유역하수도 통합플랫폼
+ 유역하수도통합플랫폼
- 통합관리시스템
+ 하수도 포탈
-
유역하수도 통합플랫폼에 오신 것을 환영합니다.
+
유역하수도통합플랫폼 하수도 포탈에 오신 것을 환영합니다.
diff --git a/apps/dm/app/(popup-layout)/map/popup/page.tsx b/apps/dm/app/(popup-layout)/map/popup/page.tsx
index ea1e4b174..e410ad2a7 100644
--- a/apps/dm/app/(popup-layout)/map/popup/page.tsx
+++ b/apps/dm/app/(popup-layout)/map/popup/page.tsx
@@ -235,6 +235,7 @@ const Map = () => {
<>
{
return (
{
- const { isOpen: isModalOpen, setIsOpen: setIsModalOpen } = useInfilChartModalStore(); // 모달 상태 관리 스토어
- const [popupOpen, setPopupOpen] = React.useState(false); // 조회조건팝업
- const buttonRef = React.useRef(null); // 버튼 참조
- const [popupPosition, setPopupPosition] = useState<{ top: number; left: number }>({ top: 0, left: 0 }); // 팝업 위치
- const [selectedAplcnYm, setSelectedAplcnYm] = useState("");
+type DataType = {
+ label: string;
+ val: number;
+};
+
+const INITIAL_ANALYSIS_OPTIONS = [{ val: 0, text: '선택' }];
+
+const InfilChart = ({ pplMngNo = '' }: ModalOpenProp) => {
+ const { isOpen: isModalOpen, setIsOpen: setIsModalOpen } = useInfilChartModalStore();
+
+ const [selectedAplcnYm, setSelectedAplcnYm] = useState('');
const [inftwtAnlss, setInftwtAnlss] = useState([]);
- const [inftwtAnlsInfos, setInftwtAnlsInfos] = useState<{ text: string; val: number; hidden?: boolean }[]>([{ val: 0, text: '선택' }]); // 침입수분석정보 options
- const [inftwtAnls, setInftwtAnls] = useState(null); // 침입수분석정보
- const [selectedInftwtAnls, setSelectedInftwtAnls] = useState(0); // 침입수분석 정보 선택시 사용
+ const [inftwtAnlsInfos, setInftwtAnlsInfos] = useState<{ text: string; val: number; hidden?: boolean }[]>(
+ INITIAL_ANALYSIS_OPTIONS,
+ );
+ const [inftwtAnls, setInftwtAnls] = useState(null);
+ const [selectedInftwtAnls, setSelectedInftwtAnls] = useState(0);
const [chartData, setChartData] = useState<{ chartData1: DataType[]; chartData2: DataType[] }>({
- chartData1: [], chartData2: [],
+ chartData1: [],
+ chartData2: [],
});
- const [inftwtQty, setInftwtQty] = useState("0"); // 침입수량
- const aplcnYms = fn.useAplcnYms('inftwtAnlsRslt'); // 상수사용월 데이터 가져오기
+ const [inftwtQty, setInftwtQty] = useState('0');
- pplMngNo = "L" + pplMngNo; // inp파일에 하수관로와 맨홀의 번호가 같으면 분석 오류가 발행하여
- const openPopup = () => {
- if (buttonRef.current) {
- const rect = buttonRef.current.getBoundingClientRect(); // 버튼 위치 계산
- setPopupPosition({
- top: rect.bottom + window.scrollY, // 버튼 아래쪽
- left: rect.left + window.scrollX, // 버튼 왼쪽
- });
- }
- setPopupOpen(true); // 팝업 열기
- };
+ const aplcnYms = fn.useAplcnYms('inftwtAnlsRslt');
+ const normalizedPplMngNo = pplMngNo ? `L${pplMngNo}` : '';
const chartForm = CHART_FORM.LINE;
const chartConfig = {
- chartData1: { label: '순오수량', color: "red", },
- chartData2: { label: '실측하수량', color: "orange", }
+ chartData1: { label: '순오수량', color: 'red' },
+ chartData2: { label: '실측하수량', color: 'orange' },
} as ChartConfig;
+ const hasChartData = useMemo(() => {
+ return chartData.chartData1.length > 0 || chartData.chartData2.length > 0;
+ }, [chartData]);
+
+ const resetState = () => {
+ setSelectedAplcnYm('');
+ setInftwtAnlss([]);
+ setInftwtAnlsInfos(INITIAL_ANALYSIS_OPTIONS);
+ setInftwtAnls(null);
+ setSelectedInftwtAnls(0);
+ setChartData({ chartData1: [], chartData2: [] });
+ setInftwtQty('0');
+ };
+
useEffect(() => {
- if (isModalOpen === false) {
- setPopupOpen(true); // 모달이 닫힐 때 조회조건팝업은 열어두기
- setSelectedAplcnYm(""); // 상수사용월 초기화
- setInftwtAnlss([]); // 침입수분석 초기화
- setInftwtAnlsInfos([{ val: 0, text: '선택' }]); // 침입수분석정보 초기화
- setInftwtAnls(null); // 침입수분석정보 초기화
- setSelectedInftwtAnls(0); // 침입수분석 일련번호 초기화
- setChartData({ chartData1: [], chartData2: [], }); // 차트 데이터 초기화
- setInftwtQty("0"); // 침입수량 초기화
+ if (!isModalOpen) {
+ resetState(); // 모달이 닫힐 때 리셋
}
}, [isModalOpen]);
- // 상수사용월 변경 핸들러
const handleAplcnYmChange = async (val: string | number) => {
const selectedValue = String(val);
setSelectedAplcnYm(selectedValue);
-
- // 즉시 관련 상태 초기화
+ setInftwtAnlss([]);
setInftwtAnls(null);
+ setSelectedInftwtAnls(0);
+ setInftwtQty('0');
+ setChartData({ chartData1: [], chartData2: [] });
- // 빈 값이면 초기화만 하고 종료
- if (!selectedValue || selectedValue === "0" || selectedValue === "") {
- setInftwtAnlsInfos([{ val: 0, text: '선택' }]);
+ if (!selectedValue || selectedValue === '0') {
+ setInftwtAnlsInfos(INITIAL_ANALYSIS_OPTIONS);
return;
}
- // API 호출
try {
const data = await fn.selectInftwtAnls(selectedValue);
- setInftwtAnlss(data ?? []);
- setInftwtAnlsInfos(fn.getOptionsInftwtAnlsInfo(data));
+ const list = data ?? [];
+ setInftwtAnlss(list);
+ setInftwtAnlsInfos(fn.getOptionsInftwtAnlsInfo(list));
} catch (error) {
- console.error("selectInftwtAnls 에러:", error);
+ console.error('selectInftwtAnls 에러:', error);
setInftwtAnlss([]);
- setInftwtAnlsInfos([{ val: 0, text: '선택' }]);
+ setInftwtAnlsInfos(INITIAL_ANALYSIS_OPTIONS);
}
- setSelectedInftwtAnls(0);
};
- // 분석정보 변경 핸들러
const handleInftwtAnlsSnChange = (val: string | number) => {
const selectedValue = Number(val);
- setSelectedInftwtAnls(selectedValue);
- // 선택된 침입수분석 정보에 대한 처리 로직
+ setSelectedInftwtAnls(selectedValue);
+ setChartData({ chartData1: [], chartData2: [] });
+ setInftwtQty('0');
+
if (selectedValue === 0) {
setInftwtAnls(null);
return;
}
- const selectedAnls = inftwtAnlss.find(anls => anls.inftwtAnlsSn === selectedValue);
- if (selectedAnls) {
- setInftwtAnls(selectedAnls);
+ const selectedAnls = inftwtAnlss.find((anls) => anls.inftwtAnlsSn === selectedValue) ?? null;
+ setInftwtAnls(selectedAnls);
+ };
+
+ const infilRsltChart = async () => {
+ if (!selectedAplcnYm || selectedInftwtAnls === 0 || !normalizedPplMngNo) {
+ setChartData({ chartData1: [], chartData2: [] });
+ setInftwtQty('0');
+ return;
+ }
+
+ try {
+ const [inftwtAnlsRslt, oilq, qty] = await Promise.all([
+ fn.selectInftwtAnlsRslt(selectedInftwtAnls, normalizedPplMngNo),
+ fn.selectOilq(selectedInftwtAnls, normalizedPplMngNo).catch((err) => {
+ console.error('selectOilq 에러:', err);
+ return [];
+ }),
+ fn.selectPplInftwtQty(selectedInftwtAnls, normalizedPplMngNo).catch((err) => {
+ console.error('selectPplInftwtQty 에러:', err);
+ return 0;
+ }),
+ ]);
+
+ setChartData({
+ chartData1: fn.getChartDataInftwtAnlsRslt(inftwtAnlsRslt ?? []),
+ chartData2: fn.getChartDataOilq(oilq ?? []),
+ });
+
+ const normalizedQty = Number(qty ?? 0);
+ setInftwtQty(normalizedQty.toLocaleString('ko-KR'));
+ } catch (error) {
+ console.error('infilRsltChart 에러:', error);
+ setChartData({ chartData1: [], chartData2: [] });
+ setInftwtQty('0');
}
};
- const closePopup = () => { // 팝업 닫기
- setPopupOpen(false);
- }
- const infilRsltChart = () => { // 그래프 & 팝업 닫기
+ const renderInfoInput = (label: string, value?: string | number | null) => (
+
+ {label}
+
+
+ );
- fn.selectInftwtAnlsRslt(selectedInftwtAnls, pplMngNo).then((data) => {// 침입수분석 결과 조회
- const rsltData = fn.getChartDataInftwtAnlsRslt(data ?? []);
- setChartData(prev => ({
- ...prev,
- chartData1: rsltData // 침입수분석결과
- }));
- });
- fn.selectOilq(selectedInftwtAnls, pplMngNo).then((data) => {// 유량 조회
- const rsltData = fn.getChartDataOilq(data ?? []);
- setChartData(prev => ({
- ...prev,
- chartData2: rsltData // 유량
- }));
- }).catch((err) => {
- console.error("selectOilq 에러:", err);
- setChartData(prev => ({
- ...prev,
- chartData2: [] // 유량 데이터 초기화
- }));
- });
- fn.selectPplInftwtQty(selectedInftwtAnls, pplMngNo).then((data) => {// 침입수량 조회
- if (data > 0) {
- setInftwtQty(data.toLocaleString('ko-KR'));
- }
- });
- setPopupOpen(false);
- };
+ const handleReset = () => {
+ resetState();
+ }
const renderBody = () => (
- <>
- 침입수분석결과
-
-
-
-
- 분석 입력현황 조회
-
- {/* 상수사용량 적용연월 */}
-
-
● 상수사용량 적용연월
-
-
- {(selectedAplcnYm) ? moment(selectedAplcnYm, "YYYYMM").format("YYYY-MM") : ""}
-
-
-
- {/* 침입수량 분석결과 */}
-
-
● 침입수량 산정결과
-
- 침입수량 :
- {inftwtQty}
- m³/d
-
-
-
- {/* 차트 */}
-
-
-
+
+
- {popupOpen && (
-
-
침입수분석 입력현황
-
-
- 상수사용월:
-
-
-
- 분석정보:
-
-
-
- 오수전환율:
-
-
-
- 시간패턴 파일명:
-
-
-
- 수용가-맨홀 매칭:
-
-
-
- 관망도 파일명:
-
-
-
-
유량 파일명:
-
+
+
+
+
+ 침입수 분석 선택 조건
+
+
+
+
+
+
+ 상수사용월:
+
+
+
+
+
+
+ 분석정보:
+
+
+
+
+ {renderInfoInput('오수전환율:', inftwtAnls?.drtwSwtcRt ?? '')}
+ {renderInfoInput('시간패턴 파일명:', inftwtAnls?.atflHrPttnNm ?? '')}
+ {renderInfoInput('수용가-맨홀 매칭:', inftwtAnls?.atflMpngNm ?? '')}
+ {renderInfoInput('관망도 파일명:', inftwtAnls?.atflInpNm ?? '')}
+ {renderInfoInput('유량 파일명:', inftwtAnls?.atflOilqNm ?? '')}
-
-
- 닫기
-
-
- 선택조건 적용
-
+
+
+
+ 조회
+
+
+ 초기화
+
-
)}
- {/* */}
- >
- )
+
+
+
+ 침입수 분석 결과
+
+
+
+
+ {/* ● */}
+ 상수사용량 적용연월
+
+
+ {selectedAplcnYm ? moment(selectedAplcnYm, 'YYYYMM').format('YYYY-MM') : '-'}
+
+
+
+
+
+ {/* ● */}
+ 침입수량 산정결과
+
+
+
침입수량
+
+ {inftwtQty}
+ m³/d
+
+
+
+
+
+
+ {hasChartData ? (
+
+ ) : (
+
+ 데이터가 없습니다.
+
+ )}
+
+
+
+
+ );
return (
{
onOpenChange={setIsModalOpen}
headerTitle=""
bodyClass={`
- min-h-[50vh]
- w-[55vw]
- max-w-none
- max-h-none
- overflow-visible
- z-[50]
- `}
+ w-[78vw]
+ max-w-none
+ min-w-[1000px]
+ min-h-[72vh]
+ overflow-visible
+ z-[50]
+ `}
body={renderBody()}
- noFooterYn='Y'
+ noFooterYn="Y"
/>
- )
-}
+ );
+};
-export default React.memo(InfilChart);
\ No newline at end of file
+export default React.memo(InfilChart);
diff --git a/apps/sn/app/(map-layout)/map/components/popupComponent/chart/rdiiChart.tsx b/apps/sn/app/(map-layout)/map/components/popupComponent/chart/rdiiChart.tsx
index 39617eb21..aa622be1b 100644
--- a/apps/sn/app/(map-layout)/map/components/popupComponent/chart/rdiiChart.tsx
+++ b/apps/sn/app/(map-layout)/map/components/popupComponent/chart/rdiiChart.tsx
@@ -1,282 +1,339 @@
'use client'
-import { CHART_FORM, type ChartConfig, MainChart, UiModal } from "@common/business_components";
+
+import React, { useEffect, useMemo, useState } from 'react';
+import moment from 'moment';
+
+import {
+ CHART_FORM,
+ type ChartConfig,
+ MainChart,
+ UiModal,
+} from '@common/business_components';
import { SelectBox } from '@common/business_components/ui';
import { Button, Input, Label } from '@common/components';
-import React, { useEffect, useState } from 'react';
-import moment from "moment";
import type { TRdiiAnls } from './aplcnYms';
import * as fn from './aplcnYms';
-import { useRdiiChartModalStore } from '../chartModalStore'; // 모달 상태 관리 스토어
+import { useRdiiChartModalStore } from '../chartModalStore';
type ModalOpenProp = {
- pplMngNo: string, // 관로관리번호
-}
-// 차트
-type lineBarChartData = {
- label : string;
- lineVal0 : number;
- barVal0 : number;
-}
+ pplMngNo?: string; // 관로관리번호
+};
+type LineBarChartData = {
+ label: string;
+ lineVal0: number;
+ barVal0: number;
+};
-const RdiiChart = ({ pplMngNo }: ModalOpenProp) => {
- const { isOpen: isModalOpen, setIsOpen: setIsModalOpen } = useRdiiChartModalStore(); // 모달 상태 관리 스토어
- const [popupOpen, setPopupOpen] = React.useState(false); // 조회조건팝업
- const buttonRef = React.useRef(null); // 버튼 참조
- const [popupPosition, setPopupPosition] = useState<{ top: number; left: number }>({ top: 0, left: 0 }); // 팝업 위치
- const [aplcnYm, setAplcnYm] = useState("");
+const INITIAL_ANALYSIS_OPTIONS = [{ val: 0, text: '선택' }];
+
+const RdiiChart = ({ pplMngNo = '' }: ModalOpenProp) => {
+ const { isOpen: isModalOpen, setIsOpen: setIsModalOpen } = useRdiiChartModalStore();
+
+ const [aplcnYm, setAplcnYm] = useState('');
const [rdiiAnlss, setRdiiAnlss] = useState([]);
- const [rdiiAnlsInfos, setRdiiAnlsInfos] = useState<{ text: string; val: number; hidden?: boolean }[]>([{ val: 0, text: '선택' }]); // 강우유입수분석정보 options
+ const [rdiiAnlsInfos, setRdiiAnlsInfos] = useState<{ text: string; val: number; hidden?: boolean }[]>(
+ INITIAL_ANALYSIS_OPTIONS,
+ );
const [rdiiAnls, setRdiiAnls] = useState(null); // 강우유입수분석정보
- const [rainInfwtAnlsSn, setRainInfwtAnlsSn] = useState(0); // 강우유입수분석 정보 선택시 사용
- const [chartData, setChartData] = useState([]);
- const [rdiiQty, setRdiiQty] = useState("0"); // 강우유입수량
- const [rainTotal, setRainTotal] = useState("0"); // 적용강우량
- const [totalVolume, setTotalVolume] = useState("0"); // 총체적 m³/mm
+ const [rainInfwtAnlsSn, setRainInfwtAnlsSn] = useState(0);// 강우유입수분석 정보 선택시 사용
+ const [chartData, setChartData] = useState([]);
+ const [rdiiQty, setRdiiQty] = useState('0'); // 강우유입수량
+ const [rainTotal, setRainTotal] = useState('0'); // 적용강우량
+ const [totalVolume, setTotalVolume] = useState('0');// 총체적 m³/mm
+
const aplcnYms = fn.useAplcnYms('rdiiAnlsRslt'); // 상수사용월 데이터 가져오기
- pplMngNo = "L"+pplMngNo; // "L13101003";
-
- const openPopup = () => {
- if (buttonRef.current) {
- const rect = buttonRef.current.getBoundingClientRect(); // 버튼 위치 계산
- setPopupPosition({
- top: rect.bottom + window.scrollY, // 버튼 아래쪽
- left: rect.left + window.scrollX, // 버튼 왼쪽
- });
- }
- setPopupOpen(true); // 팝업 열기
- };
+ const normalizedPplMngNo = pplMngNo ? `L${pplMngNo}` : '';
const chartForm = CHART_FORM.LINEBAR2;
const chartConfig = {
- barVal0: {
+ barVal0: {
label: '강수량',
- color: "#33AAFF",
+ color: '#33AAFF',
},
lineVal0: {
label: 'RDII',
- color: "#FF0000",
+ color: '#FF0000',
},
} as ChartConfig;
+ const hasChartData = useMemo(() => chartData.length > 0, [chartData]);
+
+ const resetState = () => {
+ setAplcnYm(''); // 상수사용월 초기화
+ setRdiiAnlss([]); // 강우유입수분석 초기화
+ setRdiiAnlsInfos(INITIAL_ANALYSIS_OPTIONS); // 강우유입수분석정보 초기화
+ setRdiiAnls(null); // 강우유입수분석정보 초기화
+ setRainInfwtAnlsSn(0); // 강우유입수분석 일련번호 초기화
+ setChartData([]);
+ setRdiiQty('0'); // 강우유입수량 초기화
+ setRainTotal('0');// 적용강우량 초기화
+ setTotalVolume('0');// 총체적 m³/mm 초기화
+ };
+
useEffect(() => {
- if (isModalOpen === false) {
- setPopupOpen(true); // 모달이 닫힐 때 팝업은
- setAplcnYm(""); // 상수사용월 초기화
- setRdiiAnlss([]); // 강우유입수분석 초기화
- setRdiiAnlsInfos([{ val: 0, text: '선택' }]); // 강우유입수분석정보 초기화
- setRdiiAnls(null); // 강우유입수분석정보 초기화
- setRainInfwtAnlsSn(0); // 강우유입수분석 일련번호 초기화
- setChartData([]); // 차트 데이터 초기화
- setRdiiQty("0"); // 강우유입수량 초기화
- setRainTotal("0"); // 적용강우량 초기화
- setTotalVolume("0"); // 총체적 m³/mm 초기화
+ if (!isModalOpen) {
+ resetState();
}
}, [isModalOpen]);
- // 상수사용월 변경시 분석 정보 리스트 가져오기
- useEffect(() => {
- // aplcnYm이 비어있거나 "0"이면 API 호출하지 않음
- if (!aplcnYm || aplcnYm === "0" || aplcnYm === "") {
- setRainInfwtAnlsSn(0); // 초기화
- setRdiiAnlsInfos([]);
+ const handleAplcnYmChange = async (val: string | number) => {
+ const selectedValue = String(val);
+
+ setAplcnYm(selectedValue);
+ setRdiiAnlss([]);
+ setRdiiAnls(null);
+ setRainInfwtAnlsSn(0);
+ setChartData([]);
+ setRdiiQty('0');
+ setRainTotal('0');
+ setTotalVolume('0');
+
+ if (!selectedValue || selectedValue === '0') {
+ setRdiiAnlsInfos(INITIAL_ANALYSIS_OPTIONS);
return;
}
- fn.selectRdiiAnls(aplcnYm)
- .then((data) => {
- setRdiiAnlss(data ?? []); // 선택한 월의 분석성공한 RDII분석데이터[]
- setRdiiAnlsInfos(fn.getOptionsRdiiAnlsInfo(data));
- setRdiiAnls(null); // 오수전환율 이후 초기화
- setRainInfwtAnlsSn(0); // 초기화
- })
- .catch((error) => {
- console.error("selectrdiiAnls 에러:", error);
- setRdiiAnlss([]);
- setRdiiAnlsInfos([]);
- });
- }, [aplcnYm]);
- // 값 표시
- useEffect(() => {
- // 선택된 강우유입수분석 정보에 대한 처리 로직
- if (rainInfwtAnlsSn === 0) return;
- const selectedAnls = rdiiAnlss.find(anls => anls.rainInfwtAnlsSn === rainInfwtAnlsSn);
- if (selectedAnls) {
- setRdiiAnls(selectedAnls);
+ try {
+ const data = await fn.selectRdiiAnls(selectedValue); // 상수사용월 변경시 분석 정보 리스트 가져오기
+ const list = data ?? [];
+ setRdiiAnlss(list);
+ setRdiiAnlsInfos(fn.getOptionsRdiiAnlsInfo(list));
+ } catch (error) {
+ console.error('selectRdiiAnls 에러:', error);
+ setRdiiAnlss([]);
+ setRdiiAnlsInfos(INITIAL_ANALYSIS_OPTIONS);
}
- }, [rainInfwtAnlsSn, rdiiAnlss]);
-
- const closePopup = () => { // 팝업 닫기
- setPopupOpen(false);
- }
- const rdiiRsltChart = () => { // 그래프 & 팝업 닫기
- fetchFilteredData();
- fn.selectPplRdiiQty(rainInfwtAnlsSn, pplMngNo).then((data) => {// 강우유입수량 조회
- setRdiiQty(Math.round(data?.rdiih*24).toLocaleString('ko-KR')); // cmd: m³/d 단위로 변환
- if (data?.raintotal && data?.rainTotal !== 0) {
- setRainTotal(data?.raintotal.toLocaleString('ko-KR')); // 총강우량mm
- setTotalVolume(Math.round(data?.rdiih * data?.sustaintime / data?.raintotal).toLocaleString('ko-KR')); // 총체적 m³/mm
- }
- });
- setPopupOpen(false);
};
- const fetchFilteredData: () => Promise = async () => {
- const rsltData = await fn.selectRdiiAnlsRslt(rainInfwtAnlsSn, pplMngNo);
- const rsltDataformatted = fn.getChartDataRdiiAnlsRslt(rsltData ?? []); // format 변경
- const rainData = await fn.selectRainData(rainInfwtAnlsSn);
- const rainDataformatted = fn.getChartDataRainData(rainData ?? []); // format 변경
- const rainMap = new Map(rainDataformatted.map(item => [item.label, item.barVal0]));
+ const handleRdiiAnlsSnChange = (val: string | number) => {
+ const selectedValue = Number(val);
+
+ setRainInfwtAnlsSn(selectedValue);
+ setChartData([]);
+ setRdiiQty('0');
+ setRainTotal('0');
+ setTotalVolume('0');
+
+ if (selectedValue === 0) {
+ setRdiiAnls(null);
+ return;
+ }
+
+ const selectedAnls = rdiiAnlss.find((anls) => anls.rainInfwtAnlsSn === selectedValue) ?? null;
+ setRdiiAnls(selectedAnls);
+ };
+
+ const fetchFilteredData = async () => {
+ if (!rainInfwtAnlsSn || !normalizedPplMngNo) {
+ setChartData([]);
+ setRdiiQty('0');
+ setRainTotal('0');
+ setTotalVolume('0');
+ return;
+ }
+
+ try {
+ const [rsltData, rainData, qtyData] = await Promise.all([
+ fn.selectRdiiAnlsRslt(rainInfwtAnlsSn, normalizedPplMngNo),
+ fn.selectRainData(rainInfwtAnlsSn),
+ fn.selectPplRdiiQty(rainInfwtAnlsSn, normalizedPplMngNo),
+ ]);
+
+ const rsltDataFormatted = fn.getChartDataRdiiAnlsRslt(rsltData ?? []);
+ const rainDataFormatted = fn.getChartDataRainData(rainData ?? []);
+ const rainMap = new Map(rainDataFormatted.map((item) => [item.label, item.barVal0]));
+
+ rsltDataFormatted.forEach((item) => {
+ const matchedVal = rainMap.get(item.label);
+ if (matchedVal !== undefined) {
+ item.barVal0 = matchedVal;
+ }
+ });
+
+ setChartData(rsltDataFormatted);// 차트 데이터 설정
+
+ const avgRdii = Number(qtyData?.rdiih ?? 0);
+ const appliedRainfall = Number(qtyData?.raintotal ?? 0);
+ const rdiiPerRainfall =
+ qtyData?.raintotal && qtyData?.raintotal !== 0
+ ? Math.round(avgRdii * Number(qtyData?.sustaintime ?? 0) / qtyData.raintotal)
+ : 0;
+
+ setRdiiQty(Math.round(avgRdii * 24).toLocaleString('ko-KR'));
+ setRainTotal(appliedRainfall.toLocaleString('ko-KR'));
+ setTotalVolume(rdiiPerRainfall.toLocaleString('ko-KR'));
+ } catch (error) {
+ console.error('rdiiRsltChart 에러:', error);
+ setChartData([]);
+ setRdiiQty('0');
+ setRainTotal('0');
+ setTotalVolume('0');
+ }
+ };
+
+ const renderInfoInput = (label: string, value?: string | number | null) => (
+
+
+ {label}
+
+
+
+ );
- rsltDataformatted.forEach(item => {
- const matchedVal = rainMap.get(item.label);
- if (matchedVal !== undefined) {
- item.barVal0 = matchedVal;
- }
- });
- setChartData(rsltDataformatted); // 차트 데이터 설정
- }
const renderBody = () => (
- <>
- RDII분석결과
-
-
-
-
- 분석 입력현황 조회
-
- {/* 상수사용량 적용연월 */}
-
-
● 상수사용량 적용연월
-
-
- {(aplcnYm)? moment(aplcnYm, "YYYYMM").format("YYYY-MM"):""}
-
-
-
-
- {/* 강우유입수량 분석결과 */}
-
-
● 강우유입수량 분석결과
-
- 평균RDII :
- {rdiiQty}
- m³/d
-
-
- 적용강우량 :
- {rainTotal}
- mm
-
-
- 강우량대비RDII :
- {totalVolume}
- m³/mm
-
-
-
- {/* 차트 */}
-
-
-
+
+
- {popupOpen && (
-
-
강우유입수분석 입력현황
-
-
- 상수사용월:
- {
- setAplcnYm(String(val));
- }}
- />
-
-
- 분석정보:
- {
- setRainInfwtAnlsSn(Number(val));
- }}
- />
-
-
- 오수전환율:
-
-
-
- 수용가-맨홀 매칭:
-
-
-
- 관망도 파일명:
-
-
-
- 강우 파일명:
-
-
-
-
RTK계수 출처:
-
+
+
+
+
+ 강우유입수분석 선택 조건
+
+
+
+
+
+
+ 상수사용월:
+
+
+
+
+
+
+ 분석정보:
+
+
+
+
+ {renderInfoInput('오수전환율:', rdiiAnls?.drtwSwtcRt ?? '')}
+ {renderInfoInput('수용가-맨홀 매칭:', rdiiAnls?.atflMpngNm ?? '')}
+ {renderInfoInput('관망도 파일명:', rdiiAnls?.atflInpNm ?? '')}
+ {renderInfoInput('강우 파일명:', rdiiAnls?.atflRainNm ?? '')}
+ {renderInfoInput('RTK계수 출처:', rdiiAnls?.srcNm ?? '')}
-
-
- 닫기
-
-
- 선택조건 적용
-
+
+
+
+ 조회
+
+
+
+ 초기화
+
-
)}
- {/* */}
- >
- )
+
+
+
+
+ 강우유입수분석 결과
+
+
+
+ {/* 좌측 카드 */}
+
+
+ 상수사용량 적용연월
+
+
+ {aplcnYm ? moment(aplcnYm, 'YYYYMM').format('YYYY-MM') : '-'}
+
+
+
+ {/* 우측 카드 */}
+
+ {/* 좌측: 제목 (세로 가운데 정렬) */}
+
+
+ {/* 우측: 지표 영역 */}
+
+
+
+ 평균RDII
+
+
+ {rdiiQty}
+
+
+ m³/d
+
+
+
+
+
+ 적용 강우량
+
+
+ {rainTotal}
+
+
+ mm
+
+
+
+
+
+ 강우량 대비 RDII
+
+
+ {totalVolume}
+
+
+ m³/mm
+
+
+
+
+
+
+
+ {hasChartData ? (
+
+ ) : (
+
+ 데이터가 없습니다.
+
+ )}
+
+
+
+
+ );
return (
{
onOpenChange={setIsModalOpen}
headerTitle=""
bodyClass={`
- min-h-[50vh]
- w-[55vw]
- max-w-none
- max-h-none
- overflow-visible
- z-[50]
- `}
+ w-[78vw]
+ max-w-none
+ min-w-[1100px]
+ min-h-[72vh]
+ overflow-visible
+ z-[50]
+ `}
body={renderBody()}
- noFooterYn = 'Y'
+ noFooterYn="Y"
/>
- )
-}
+ );
+};
-export default React.memo(RdiiChart);
\ No newline at end of file
+export default React.memo(RdiiChart);
diff --git a/apps/sn/app/(map-layout)/map/page.tsx b/apps/sn/app/(map-layout)/map/page.tsx
index 73f8ae8c7..75b2d573e 100644
--- a/apps/sn/app/(map-layout)/map/page.tsx
+++ b/apps/sn/app/(map-layout)/map/page.tsx
@@ -171,6 +171,7 @@ const Map = () => {
setIsPlaying={setIsPlaying as any}
targetLayerTypes={WATER_DEPTH_LAYER_TYPES as any}
checkInterval={2500}
+ showZoomLevel={true}
defaultZoom={7.5}
defaultLonLat={[127.75, 36]}
diff --git a/apps/sn/public/images/map/layericon/RDL_TSCT_PS.png b/apps/sn/public/images/map/layericon/RDL_TSCT_PS.png
index ffc225159..81ad25a20 100644
Binary files a/apps/sn/public/images/map/layericon/RDL_TSCT_PS.png and b/apps/sn/public/images/map/layericon/RDL_TSCT_PS.png differ
diff --git a/apps/sn/public/images/map/layericon/RDL_TSCT_PS500.png b/apps/sn/public/images/map/layericon/RDL_TSCT_PS500.png
new file mode 100644
index 000000000..991826693
Binary files /dev/null and b/apps/sn/public/images/map/layericon/RDL_TSCT_PS500.png differ
diff --git a/apps/st/src/components/statistics/verification/ImrVerificationHistory.tsx b/apps/st/src/components/statistics/verification/ImrVerificationHistory.tsx
index 852d21d08..9592e0cb1 100644
--- a/apps/st/src/components/statistics/verification/ImrVerificationHistory.tsx
+++ b/apps/st/src/components/statistics/verification/ImrVerificationHistory.tsx
@@ -23,8 +23,9 @@ import {
import dayjs from "dayjs";
import { ENDPOINT } from "@/shared/config";
-import { CodeApi, CodeSelect } from "@/entities/code";
+import { CodeSelect } from "@/entities/code";
import type {
+ AdstZoneResponseDto,
StAnlsMstResponse,
StAnlsMstSearchParams,
StAnlsRsltResponse,
@@ -35,6 +36,10 @@ import {
} from "@/entities/statistics";
import { stAnlsMstApi } from "@/entities/statistics/api/statistics-analysis-master-api";
import { imrApi } from "@/entities/statistics/api/imr-api";
+import {
+ useStAnlsSidoCombo,
+ useStAnlsSigunguCombo,
+} from "@/entities/statistics/api/queries";
import { stAnlsRsltApi } from "@/entities/statistics/api/statistics-analysis-result-api";
import {
Button,
@@ -67,6 +72,7 @@ type DateRangeValue = {
};
const EMPTY_DATA: never[] = [];
+const EMPTY_ADST_LIST: AdstZoneResponseDto[] = [];
const ImrVerificationHistory = () => {
// Area A Search States
@@ -103,25 +109,10 @@ const ImrVerificationHistory = () => {
pageSize: 5,
});
- // Codes for Filter
- const { data: lwpoCodes } = useQuery({
- queryKey: [ENDPOINT.CMS_SERVICE.CODE, "LAESECD"],
- queryFn: () => CodeApi.getCode("LAESECD"),
- });
-
- const sidoOptions = useMemo(() => {
- if (!lwpoCodes) return [];
- return lwpoCodes.filter(
- (c) => c.lwpoCd.startsWith("G3") && c.lwpoCd.length === 4,
- );
- }, [lwpoCodes]);
-
- const sigunguOptions = useMemo(() => {
- if (!lwpoCodes || searchSido === "") return [];
- return lwpoCodes.filter(
- (c) => c.lwpoCd.startsWith(searchSido) && c.lwpoCd.length === 7,
- );
- }, [lwpoCodes, searchSido]);
+ const { data: sidoData } = useStAnlsSidoCombo();
+ const { data: sigunguData } = useStAnlsSigunguCombo(searchSido);
+ const sidoOptions = sidoData ?? EMPTY_ADST_LIST;
+ const sigunguOptions = sigunguData ?? EMPTY_ADST_LIST;
// Area A Data Fetching
const { data: historyData } = useQuery({
@@ -369,10 +360,10 @@ const ImrVerificationHistory = () => {
{sidoOptions.map((opt) => (
- {opt.lwpoCdNm}
+ {opt.adstNm}
))}
@@ -394,10 +385,10 @@ const ImrVerificationHistory = () => {
{sigunguOptions.map((opt) => (
- {opt.lwpoCdNm}
+ {opt.adstNm}
))}
diff --git a/apps/st/src/entities/statistics/api/statistics-analysis-detail-api.ts b/apps/st/src/entities/statistics/api/statistics-analysis-detail-api.ts
index 4e7a6e476..957680ec2 100644
--- a/apps/st/src/entities/statistics/api/statistics-analysis-detail-api.ts
+++ b/apps/st/src/entities/statistics/api/statistics-analysis-detail-api.ts
@@ -5,7 +5,7 @@ import type {
StAnlsDtlListApiResponse,
StAnlsDtlResponse,
StAnlsDtlSearchParams,
-} from "../model/statistics-analysis-detail-interface";
+} from "../model/statistics-analysis/statistics-analysis-detail-interface";
export class StAnlsDtlApiService {
public async getDetailList(
diff --git a/apps/st/src/entities/statistics/api/statistics-analysis-master-api.ts b/apps/st/src/entities/statistics/api/statistics-analysis-master-api.ts
index 19a6bee1c..bcbabda99 100644
--- a/apps/st/src/entities/statistics/api/statistics-analysis-master-api.ts
+++ b/apps/st/src/entities/statistics/api/statistics-analysis-master-api.ts
@@ -40,7 +40,9 @@ export class StAnlsMstApiService {
return response.data;
}
- public async getSigunguCombo(sidoCd: string): Promise {
+ public async getSigunguCombo(
+ sidoCd: string,
+ ): Promise {
const response = await client.get(
`${ENDPOINT.STATVERIFY_SERVICE.ST_ANLS_MST}/sigungu`,
{ params: { sidoCd } },
diff --git a/apps/st/src/entities/statistics/index.ts b/apps/st/src/entities/statistics/index.ts
index 44ce398c1..08f32d08b 100644
--- a/apps/st/src/entities/statistics/index.ts
+++ b/apps/st/src/entities/statistics/index.ts
@@ -8,7 +8,6 @@ export {
type OutlierRecordDto,
} from "./model/analysis/analysis-interface";
export { imrDetailGridColumnInfo } from "./model/imr/imr-detail-grid-column-info";
-export { imrDetailGridColumns } from "./model/imr/imr-detail-grid-columns";
export {
type ImrDetailRequest,
type ImrDetailResponse,
diff --git a/apps/st/src/entities/statistics/model/imr/imr-detail-grid-columns.tsx b/apps/st/src/entities/statistics/model/imr/imr-detail-grid-columns.tsx
deleted file mode 100644
index 80aef8bef..000000000
--- a/apps/st/src/entities/statistics/model/imr/imr-detail-grid-columns.tsx
+++ /dev/null
@@ -1,78 +0,0 @@
-import { createColumnHelper } from "@tanstack/react-table";
-
-import type { StAnlsRsltResponse } from "../statistics-analysis/statistics-analysis-result-interface";
-
-const columnHelper = createColumnHelper();
-
-export const imrDetailGridColumns = [
- columnHelper.accessor("sido", {
- header: "시도",
- cell: (info) => (
- {info.getValue() ?? "-"}
- ),
- }),
- columnHelper.accessor("gugun", {
- header: "구군",
- cell: (info) => (
- {info.getValue() ?? "-"}
- ),
- }),
- columnHelper.accessor("admdstNm", {
- header: "행정구역",
- cell: (info) => (
- {info.getValue() ?? "-"}
- ),
- }),
- columnHelper.accessor("admdstCd", {
- header: "시설코드",
- cell: (info) => (
- {info.getValue() ?? "-"}
- ),
- }),
- columnHelper.accessor("fcltNm", {
- header: "시설명",
- cell: (info) => (
- {info.getValue() ?? "-"}
- ),
- }),
- columnHelper.accessor("addr", {
- header: "주소",
- cell: (info) => (
-
- {info.getValue() ?? "-"}
-
- ),
- }),
- columnHelper.accessor("pipeSeCd", {
- header: "관로구분",
- cell: (info) => (
- {info.getValue() ?? "-"}
- ),
- }),
- columnHelper.accessor("vrfcTrgtSn", {
- header: "항목번호",
- cell: (info) => (
- {info.getValue() ?? "-"}
- ),
- }),
- columnHelper.display({
- id: "statsNm",
- header: "항목명",
- cell: (info) => {
- const statsNm = (info.table.options.meta as { statsNm?: string })
- ?.statsNm;
- return {statsNm ?? "-"}
;
- },
- }),
- columnHelper.accessor("cryrVl", {
- header: "당해년도",
- cell: (info) => (
-
- {info.getValue()?.toLocaleString() ?? "-"}
-
- ),
- }),
-];
diff --git a/apps/st/src/entities/statistics/model/statistics-analysis/statistics-analysis-master-interface.ts b/apps/st/src/entities/statistics/model/statistics-analysis/statistics-analysis-master-interface.ts
index 046ba891b..781beeb1c 100644
--- a/apps/st/src/entities/statistics/model/statistics-analysis/statistics-analysis-master-interface.ts
+++ b/apps/st/src/entities/statistics/model/statistics-analysis/statistics-analysis-master-interface.ts
@@ -57,7 +57,7 @@ export interface StAnlsMstResponseDto {
/**
* 분석 현황 마스터 검색 DTO
*/
-export interface StAnlsMstSearchDto extends PageableParams {
+export interface StAnlsMstSearchDto extends PageableParams {
/** 분석상태코드 (COMPLETED, FAILED, QUEUED, PROCESSING) */
anlsSttsCd?: string;
/** 작업UUID */
diff --git a/apps/st/src/pages/auth/login/ui/login-page.tsx b/apps/st/src/pages/auth/login/ui/login-page.tsx
index 5ce6c9cb6..8d2b3300a 100644
--- a/apps/st/src/pages/auth/login/ui/login-page.tsx
+++ b/apps/st/src/pages/auth/login/ui/login-page.tsx
@@ -22,11 +22,11 @@ function LoginPage() {
- 하수도정보시스템
+ 유역하수도통합플랫폼
- CMS 통합관리시스템
+ 하수도통계 관리시스템
-
하수도정보시스템 CMS 통합관리시스템에 오신 것을 환영합니다.
+
유역하수도통합플랫폼 하수도통계 관리시스템에 오신 것을 환영합니다.
diff --git a/apps/wr/app/(map-layout)/map/mainMap/page.tsx b/apps/wr/app/(map-layout)/map/mainMap/page.tsx
index 927703ad9..652fe1924 100644
--- a/apps/wr/app/(map-layout)/map/mainMap/page.tsx
+++ b/apps/wr/app/(map-layout)/map/mainMap/page.tsx
@@ -388,6 +388,7 @@ const Map = () => {
return (
{
<>
state);
const { coMatRvwRes, coMatRvwCount } = coRvw;
const { regAuthrtYn } = auth;
+ const alert = useAlertStore((state) => state.setMessage);
//init, sub, match
const initRef = useRef(false);
@@ -253,7 +254,7 @@ export default function CoMatRvw() {
const fromNum = Number(fromDate);
const toNum = Number(toDate);
if (fromNum > toNum) {
- toast.error(
+ alert(
"시작년도가 종료년도 이후로 설정할 수 없습니다."
);
} else {
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 d76549153..d315b2c25 100644
--- a/apps/wr/app/(with-layout)/matMng/coMat/ruseCaseMng/page.tsx
+++ b/apps/wr/app/(with-layout)/matMng/coMat/ruseCaseMng/page.tsx
@@ -21,8 +21,9 @@ import {
} from "@common/business_components/ui";
import ListDiv from "@common/business_components/ui/list_div";
import { Button, Label } from "@common/components/ui";
+import { useAlertStore } from "@/src/shared/lib";
import { useEffect, useState } from "react";
-import toast, { Toaster } from "react-hot-toast";
+import { Toaster } from "react-hot-toast";
import { useTranslation } from "react-i18next";
import * as XLSX from "xlsx";
@@ -44,6 +45,7 @@ export default function RuseCaseMng() {
const [name, setName] = useState("");
const [resetPage, isResetPage] = useState(false);
const { t } = useTranslation("base");
+ const alert = useAlertStore((state) => state.setMessage);
// 엑셀 등록 모달
const [isExcelOpen, setIsExcelOpen] = useState(false);
@@ -84,7 +86,7 @@ export default function RuseCaseMng() {
if (!excelList) return;
if (excelList.length === 0) {
- toast.error(t("excelDownErrMsg"));
+ alert(t("excelDownErrMsg"));
return;
}
fetch(`/sample_ruseCaseMng.xlsx`, {}).then(async (response) => {
diff --git a/apps/wr/app/(with-layout)/matMng/coMat/ruseRvwHstry/page.tsx b/apps/wr/app/(with-layout)/matMng/coMat/ruseRvwHstry/page.tsx
index 9b7737236..782becca9 100644
--- a/apps/wr/app/(with-layout)/matMng/coMat/ruseRvwHstry/page.tsx
+++ b/apps/wr/app/(with-layout)/matMng/coMat/ruseRvwHstry/page.tsx
@@ -24,9 +24,10 @@ import {
} from "@common/business_components/ui";
import ListDiv from "@common/business_components/ui/list_div";
import { Button, Label } from "@common/components/ui";
+import { useAlertStore } from "@/src/shared/lib";
import moment from "moment";
import { useCallback, useEffect, useState } from "react";
-import toast, { Toaster } from "react-hot-toast";
+import { Toaster } from "react-hot-toast";
import { useTranslation } from "react-i18next";
export default function RuseRvwHstry() {
const {
@@ -40,6 +41,7 @@ export default function RuseRvwHstry() {
} = useWrStore((state) => state);
const { rvwHistRes, ruseHistHeadList, rvwHistCount } = ruseHist;
const { regAuthrtYn } = auth;
+ const alert = useAlertStore((state) => state.setMessage);
const [pageNation, setPageNatiion] = useState(1);
@@ -79,7 +81,7 @@ export default function RuseRvwHstry() {
const fromNum = Number(fromDate);
const toNum = Number(toDate);
if (fromNum > toNum) {
- toast.error("시작년도가 종료년도 이후로 설정할 수 없습니다.");
+ alert("시작년도가 종료년도 이후로 설정할 수 없습니다.");
} else {
let con: string | number = "";
if (String(objSearch.city).length > 0) {
@@ -133,7 +135,7 @@ export default function RuseRvwHstry() {
selectedData[index]?.splyFcltFlg === "매칭완료" &&
headId === "completeYn"
) {
- toast.error(t("canNotChangeMatStateMsg"));
+ alert(t("canNotChangeMatStateMsg"));
return;
}
}
@@ -177,7 +179,7 @@ export default function RuseRvwHstry() {
//
// ));
} else {
- toast.error( t('isNotMatchFinishMsg'));
+ alert(t('isNotMatchFinishMsg'));
}
};
useEffect(() => {
diff --git a/apps/wr/app/(with-layout)/matMng/coMng/coSttn/page.tsx b/apps/wr/app/(with-layout)/matMng/coMng/coSttn/page.tsx
index f5104154f..85d9ef635 100644
--- a/apps/wr/app/(with-layout)/matMng/coMng/coSttn/page.tsx
+++ b/apps/wr/app/(with-layout)/matMng/coMng/coSttn/page.tsx
@@ -5,7 +5,7 @@ import { btnClass } from "@/app/store/common/code";
import { excelCoSttnParsing } from "@/app/store/matDmd/coSttn/excel";
import { fetchType, getFetch } from "@/components/lib/api";
import {
- confirmToast,
+ //confirmToast,
excelDownload,
formatPhoneNumber,
getCity,
@@ -29,8 +29,10 @@ import type { DemandCompType } from "@common/business_components/ui/sdmInsertFor
import { Button } from "@common/components/ui";
import moment from "moment";
import { useCallback, useEffect, useState } from "react";
-import toast, { Toaster } from "react-hot-toast";
+// import toast, { Toaster } from "react-hot-toast";
import { useTranslation } from "react-i18next";
+import { useAlertStore } from "@/src/shared/lib";
+import { useConfirm } from "@frontend-opensource/use-react-hooks";
export default function CoSttn() {
const {
@@ -48,7 +50,8 @@ export default function CoSttn() {
getMyUUID
} = useWrStore((state) => state);
const { regAuthrtYn, mdfcnAuthrtYn } = auth;
-
+ const alert = useAlertStore((state) => state.setMessage);
+ const { confirm } = useConfirm();
const [bdstTable, setBdstTable] = useState();
const [pageNation, setPageNation] = useState(1);
const { coSttnRes, coSttnResCount } = coSttn;
@@ -98,7 +101,7 @@ export default function CoSttn() {
useEffect(() => {
if (!coSttn.isInit) {
- coSttnInit("1", "13", "0");
+ coSttnInit("1", "13", "0"); // 수요처 목록 조회
}
}, [coSttn.isInit, coSttnInit]);
@@ -167,16 +170,21 @@ export default function CoSttn() {
}
};
const { t } = useTranslation('base');
- const initPassword = () => {
+ const initPassword = async () => {
if (updateData) {
const dmdInfoId = updateData.dmdInfoId;
const dmdInfoSliceId = `${dmdInfoId.slice(0, 3)}-${dmdInfoId.slice(3, 5)}-${dmdInfoId.slice(5, 10)}-${dmdInfoId.slice(10)}`
- const handleConfirm = () => {
- coSttnPwInit(dmdInfoId);
+ const handleConfirm = async () => {
+ const message = await coSttnPwInit(dmdInfoId);
+ alert(message);
};
- confirmToast( t('isInitPw', { dmdInfoId: dmdInfoSliceId }), handleConfirm, 'text-2xl');
+ //confirmToast( t('isInitPw', { dmdInfoId: dmdInfoSliceId }), handleConfirm, 'text-2xl');
+ if (await confirm(t('isInitPw', { dmdInfoId: dmdInfoSliceId }))) {
+ await handleConfirm();
+ }
} else {
- toast.error("비밀번호 초기화를 원하는 행을 선택해주세요.")
+ alert("비밀번호 초기화를 원하는 행을 선택해주세요.");
+ // toast.error("비밀번호 초기화를 원하는 행을 선택해주세요.")
}
}
@@ -238,7 +246,7 @@ export default function CoSttn() {
onClick={() => {
/* 저장 로직 */
if (!updateData) {
- toast.error("수요처 변경을 원하는 행을 선택해주세요");
+ alert("수요처 변경을 원하는 행을 선택해주세요");
} else {
setIsDmdModal({
...isDmdModal,
@@ -274,8 +282,8 @@ export default function CoSttn() {
headName={[]}
tableData={bdstTable || []}
headlist={coSttn.coSttHeadList}
- pageSize={20}
- total={coSttnResCount + 10 || 0}
+ pageSize={13}
+ total={coSttnResCount || 0}
cellClick={ async(index: number | undefined) => {
const uuid = await getMyUUID();
const updateData = coSttnRes[index as number];
@@ -319,7 +327,8 @@ export default function CoSttn() {
? objSearch.sgg
: objSearch.city;
if (updateData?.dmdInfoId) {
- await updateDemandCompany(data as DemandCompType);
+ const message = await updateDemandCompany(data as DemandCompType);
+ alert(message);
coSttnInit("1", "13", String(con));
setIsDmdModal({
isMoal: false,
@@ -332,12 +341,11 @@ export default function CoSttn() {
`/reuseFc/request/${data.dmdInfoId}`
);
if (dmd) {
- toast.error(
- `사업자번호 ${dmd.dmdInfoId}는 이미 등록되었습니다. 다시등록해주세요`
- );
+ alert(`사업자번호 ${dmd.dmdInfoId}는 이미 등록되었습니다. 다시등록해주세요`);
return;
}
- await regDemandCompany(data as DemandCompType);
+ const message = await regDemandCompany(data as DemandCompType);
+ alert(message);
coSttnInit("1", "13", String(con));
setIsDmdModal({
isMoal: false,
@@ -352,7 +360,7 @@ export default function CoSttn() {
}
>
-
+ {/* */}
>
);
}
diff --git a/apps/wr/app/(with-layout)/matMng/coMng/drgtSttn/page.tsx b/apps/wr/app/(with-layout)/matMng/coMng/drgtSttn/page.tsx
index b55878487..b4ca64268 100644
--- a/apps/wr/app/(with-layout)/matMng/coMng/drgtSttn/page.tsx
+++ b/apps/wr/app/(with-layout)/matMng/coMng/drgtSttn/page.tsx
@@ -9,12 +9,14 @@ import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/b
import { ContentsDiv, DateMonthPicker, downloadExcel, SearchDiv, SearchFormLeft, SearchFormRight, SelectBox, UiModal, UiTable, type SelectDataType } from "@common/business_components/ui";
import ListDiv from "@common/business_components/ui/list_div";
import { Button, Label, ScrollArea } from "@common/components/ui";
+import { useAlertStore } from "@/src/shared/lib";
import { useEffect, useState, useRef } from "react";
-import { toast, Toaster } from "react-hot-toast";
+import { Toaster } from "react-hot-toast";
export default function DrgtSttn() {
const { code, drgt, drgtSearch, drgtModalSearch, drgtInit } = useWrStore((state) => state);
const { drgtRes, drgtMoRes } = drgt;
+ const alert = useAlertStore((state) => state.setMessage);
const [fromDate, setFromDate] = useState('202506');
const [toDate, setToDate] = useState('202506');
const [selectSgg, setSelectSgg] = useState([{ val: '0', text: '전체' }]);
@@ -32,7 +34,7 @@ export default function DrgtSttn() {
if (fromDate && toDate && drgtRes[index]) {
drgtModalSearch(fromDate, toDate, drgtRes[index].sggNm ?? '');
} else {
- toast.error("조회기간을 선택해주세요.");
+ alert("조회기간을 선택해주세요.");
}
}
diff --git a/apps/wr/app/(with-layout)/matMng/coMng/ldrmntCoSttn/page.tsx b/apps/wr/app/(with-layout)/matMng/coMng/ldrmntCoSttn/page.tsx
index eee7d9f73..13b596b5c 100644
--- a/apps/wr/app/(with-layout)/matMng/coMng/ldrmntCoSttn/page.tsx
+++ b/apps/wr/app/(with-layout)/matMng/coMng/ldrmntCoSttn/page.tsx
@@ -27,8 +27,9 @@ import {
} from "@common/business_components/ui";
import ListDiv from "@common/business_components/ui/list_div";
import { Button } from "@common/components/ui";
+import { useAlertStore } from "@/src/shared/lib";
import { useCallback, useEffect, useState } from "react";
-import toast, { Toaster } from "react-hot-toast";
+import { Toaster } from "react-hot-toast";
import { useTranslation } from "react-i18next";
export default function LdrmntCoSttn() {
@@ -46,6 +47,7 @@ export default function LdrmntCoSttn() {
const [resetPage, isResetPage] = useState(false);
const [isDownloadClicked, setIsDownloadClicked] = useState(false);
const { t } = useTranslation('base');
+ const alert = useAlertStore((state) => state.setMessage);
const { regAuthrtYn, mdfcnAuthrtYn } = auth;
const excelClick = useCallback(() => {
@@ -113,7 +115,7 @@ export default function LdrmntCoSttn() {
headerRange: "A1:F1"
});
} else {
- toast.error(t('excelDownErrMsg'));
+ alert(t('excelDownErrMsg'));
}
setIsDownloadClicked(false);
}, [excelData, isDownloadClicked]);
@@ -270,7 +272,8 @@ export default function LdrmntCoSttn() {
readonly={mdfcnAuthrtYn === 'N'}
onFormSubmit={async (value) => {
const handleConfirm = async () => {
- await ldrmntUpdate(value);
+ const message = await ldrmntUpdate(value);
+ alert(message);
setDetailModal({ ...detailModal, isOpen: false });
ldrmntSearch(false, pageNation, '20', computeCityNm(), searchTxt || "");
};
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 396edb8f0..9ad16e84d 100644
--- a/apps/wr/app/(with-layout)/ruseFcltMng/fcltReg/pbnstReg/page.tsx
+++ b/apps/wr/app/(with-layout)/ruseFcltMng/fcltReg/pbnstReg/page.tsx
@@ -44,13 +44,14 @@ import {
import { PbnstMngForm } from "@/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm";
import { PbnstBuseForm } from "@/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm";
import { PbnstRegModal } from "@/components/ui/modal/fcltReg/pbnstRegModal/pbnstRegModal";
-import toast, { Toaster } from "react-hot-toast"; // toast 이미 import 되어 있음
+import { Toaster } from "react-hot-toast";
import { writeRgtrAndMdfr } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg";
import { useFieldArray, useForm, type FieldErrors } from "react-hook-form"; // FieldErrors 타입 추가
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { createDefaultPbnstItem } from "./utilsZod";
import { useTranslation } from "react-i18next";
+import { useAlertStore } from "@/src/shared/lib";
export default function PublicReuseFaciltyInsert() {
const {
auth,
@@ -66,6 +67,7 @@ export default function PublicReuseFaciltyInsert() {
} = useWrStore((state) => state);
const { t } = useTranslation("base");
+ const alert = useAlertStore((state) => state.setMessage);
const { pubFacHeadList, pbnstHasuResCount, pbnstHasuRes, pbnstRes } = pbnst;
const { regAuthrtYn, mdfcnAuthrtYn, laeSeCd } = auth;
const [, setCurrentTab] = useState("mng");
@@ -140,7 +142,7 @@ export default function PublicReuseFaciltyInsert() {
const isFcltSuccess = resFclt.status === "fulfilled";
const isBizSuccess = resBiz.status === "fulfilled";
if (isFcltSuccess && isBizSuccess) {
- toast.success("하수 재이용 시설 및 사업이 수정되었습니다. ");
+ alert("하수 재이용 시설 및 사업이 수정되었습니다.");
// 메시지 표시, 페이지 이동 등
} else {
let message = "";
@@ -151,7 +153,7 @@ export default function PublicReuseFaciltyInsert() {
if (!isBizSuccess) {
message += "하수재이용 시설 사업 ";
}
- toast.error(message + "오류 입니다 ");
+ alert(message + "오류 입니다 ");
pubHasuInit("1", laeSeCd);
}
});
@@ -215,7 +217,7 @@ export default function PublicReuseFaciltyInsert() {
}
}
// 최종 오류 메시지 출력
- toast.error(`${prefix}${message}`);
+ alert(`${prefix}${message}`);
});
}
};
@@ -321,8 +323,8 @@ export default function PublicReuseFaciltyInsert() {
onClick={() => {
// pbnstMngData([] as customPbnst[]);
if (!selectRow) {
- toast.error(
- "하수리시시설을 테이블을 선택하고 등록버튼을 눌러주세요"
+ alert(
+ "재시용시설을 등록할 하수리시시설을 테이블에서 선택하고 등록버튼을 눌러주세요"
);
return;
} else {
@@ -416,7 +418,7 @@ export default function PublicReuseFaciltyInsert() {
onClick={() => {
// pbnstMngData([] as customPbnst[]);
if (!selectRow) {
- toast.error(
+ alert(
"하수리시시설을 테이블을 선택하고 등록버튼을 눌러주세요"
);
return;
diff --git a/apps/wr/app/store/common/searchCode/index.ts b/apps/wr/app/store/common/searchCode/index.ts
index 03d1b0fc5..1478b10a8 100644
--- a/apps/wr/app/store/common/searchCode/index.ts
+++ b/apps/wr/app/store/common/searchCode/index.ts
@@ -7,7 +7,6 @@ import {
import type { pubHasuType } from "../../ruseFcltMng/fcltReg/pbnstReg/type";
import type { DemandCompType } from "@common/business_components/ui/sdmInsertForm/form";
import type { conCostFeeType, rateFeeType } from "./type";
-import toast from "react-hot-toast";
export const comFcltStateExport: comFcltState = {
@@ -59,8 +58,8 @@ export type comFcltState = {
type comFcltActions = {
getSwtrmtFcltNm: (searchNm: string) => void;
getDemandCompList: (searchNm: string, cnt: number) => void;
- regDemandCompany: (data: DemandCompType) => void;
- updateDemandCompany: (data: DemandCompType) => void;
+ regDemandCompany: (data: DemandCompType) => Promise;
+ updateDemandCompany: (data: DemandCompType) => Promise;
getConstCostFee: (year: string) => void;
getRateFee: (year: string) => void;
};
@@ -109,7 +108,6 @@ export const comFcltSearchAction: (set: any) => comFcltActions = (set: any) => {
fetchType.WR
);
if (response.status === 200) {
- toast.success('수요처가 등록되었습니다. ')
set((state: comFcltState) => {
return {
@@ -117,11 +115,12 @@ export const comFcltSearchAction: (set: any) => comFcltActions = (set: any) => {
dmdIsSucees: "success"
}
});
+ return "수요처가 등록되었습니다.";
}
+ return "수요처 등록에 실패하였습니다.";
} catch (err) {
console.log(err);
- toast.error('수요처가 등록 오류입니다.. ')
-
+ return "수요처 등록 중 오류가 발생했습니다.";
}
},
updateDemandCompany: async (data: DemandCompType) => {
@@ -131,7 +130,6 @@ export const comFcltSearchAction: (set: any) => comFcltActions = (set: any) => {
let message = "";
if (response.status === 200) {
message = "success";
- toast.success('수요처 정보가 변경되었습니다. ' )
} else {
message = "fail";
}
@@ -142,10 +140,13 @@ export const comFcltSearchAction: (set: any) => comFcltActions = (set: any) => {
dmdIsSucees: message
}
});
+ if (response.status === 200) {
+ return "수요처 정보가 변경되었습니다.";
+ }
+ return "수요처 정보 변경에 실패하였습니다.";
} catch (err) {
console.log(err);
- toast.success('수요처 정보가 오류입니다. ' )
-
+ return "수요처 정보 변경 중 오류가 발생했습니다.";
}
},
getRateFee: async (year: string) => {
diff --git a/apps/wr/app/store/matDmd/coMatRvw/index.tsx b/apps/wr/app/store/matDmd/coMatRvw/index.tsx
index 9e857d4d3..0c58db9a6 100644
--- a/apps/wr/app/store/matDmd/coMatRvw/index.tsx
+++ b/apps/wr/app/store/matDmd/coMatRvw/index.tsx
@@ -1,5 +1,4 @@
import { fetchType, getFetchWithCount, putFectch } from "@/components/lib/api";
-import toast from "react-hot-toast";
import type { CommonTableProps } from "../../common/type/header";
import {
constCostHeadListExport,
@@ -50,7 +49,7 @@ type coRvwActions = {
splyCd: string,
dmdCd: string,
data: DedMatUpdateType
- ) => void;
+ ) => Promise;
coMatRvwSearch: (
pageNo: number,
pageCnt: number,
@@ -104,20 +103,17 @@ export const coRvwActionsExport: (set: any) => coRvwActions = (set: any) => {
splyCd: string,
dmdCd: string,
data: DedMatUpdateType
- ) => {
+ ): Promise => {
try {
const response = await putFectch(
`/reuseFc/request/cst/${splyCd}/${dmdCd}`,
data
);
if (response.status === 200) {
-
- if(data.flg ==='Sub'){
-
- toast.success("매칭 검토로 저장되었습니다.");
- }else{
- toast.success("매칭 검토완료로 저장되었습니다.");
- }
+ const successMessage =
+ data.flg === "Sub"
+ ? "매칭 검토로 저장되었습니다."
+ : "매칭 검토완료로 저장되었습니다.";
const coMatRvwRes = await coMatInit(
1,
@@ -125,21 +121,22 @@ export const coRvwActionsExport: (set: any) => coRvwActions = (set: any) => {
new Date().getFullYear().toString(),
new Date().getFullYear().toString()
);
- return set((state: coRvwState) => ({
+ set((state: coRvwState) => ({
...state,
coRvw: {
...state.coRvw,
coMatRvwRes: coMatRvwRes.data,
- coMatRvwCount:coMatRvwRes.count,
+ coMatRvwCount: coMatRvwRes.count,
init: false,
},
}));
+ return successMessage;
} else {
- toast.error("매칭 검토 저장에 실패했습니다.");
+ return "매칭 검토 저장에 실패했습니다.";
}
} catch (e) {
- toast.error("매칭 검토 저장 오류입니다.");
console.error("coMatRvwUpdate error", e);
+ return "매칭 검토 저장 오류입니다.";
}
},
coMatRvwSearch: async (
diff --git a/apps/wr/app/store/matDmd/coSttn/index.tsx b/apps/wr/app/store/matDmd/coSttn/index.tsx
index 1f7b2c54b..ca610c381 100644
--- a/apps/wr/app/store/matDmd/coSttn/index.tsx
+++ b/apps/wr/app/store/matDmd/coSttn/index.tsx
@@ -2,7 +2,6 @@ import type { CommonTableProps } from "@/app/store/common/type/header";
import { fetchType, getFetchWithCount, putFectch } from "@/components/lib/api";
import type { DemandCompType } from "@common/business_components/ui/sdmInsertForm/form";
import { coSttHeadListExport, coSttNodeListExport } from "./headList";
-import toast from "react-hot-toast";
// 테이블 헤드 리스트
const coSttNodeList = coSttNodeListExport;
@@ -25,7 +24,7 @@ type coSttnActions = {
coSttnInit: (pageNo: string, pageCnt: string, con: string) => void;
coSttnSearch: (pageNo: string, pageCnt: string, con: string) => void;
coSttnTxtSearch: (pageNo: string, pageCnt: string, con: string, txt: string) => void;
- coSttnPwInit: (dmdInfoId: string) => void;
+ coSttnPwInit: (dmdInfoId: string) => Promise;
};
export type coSttnType = coSttnState & coSttnActions;
@@ -46,6 +45,7 @@ export const coSttnActionsExport: (set: any) => coSttnActions = (
set: any
) => {
return {
+ // 수요처 목록 조회
coSttnInit: async (pageNo: string, pageCnt: string, con: string) => {
try {
@@ -126,10 +126,14 @@ export const coSttnActionsExport: (set: any) => coSttnActions = (
try {
const response = await putFectch(`/reuseFc/request/chgPassword/${dmdInfoId}`,[])
if (response.status === 200) {
- toast.success('비밀번호가 초기화 되었습니다.')
+ return "비밀번호가 초기화되었습니다.";
} else {
- toast.error('비밀번호 초기화에 실패하였습니다')
+ return "비밀번호 초기화에 실패하였습니다.";
}
+ } catch (err) {
+ console.log(err);
+ return "비밀번호 초기화에 실패하였습니다.";
+ } finally {
set((state: coSttnState) => {
return {
...state,
@@ -139,11 +143,7 @@ export const coSttnActionsExport: (set: any) => coSttnActions = (
}
}
});
- } catch (err) {
- console.log(err);
- toast.error('비밀번호 초기화에 실패하였습니다.')
-
}
}
}
-}
\ No newline at end of file
+}
diff --git a/apps/wr/app/store/matDmd/ldrmntCoSttn/headList.tsx b/apps/wr/app/store/matDmd/ldrmntCoSttn/headList.tsx
index 294371c44..bb7c79024 100644
--- a/apps/wr/app/store/matDmd/ldrmntCoSttn/headList.tsx
+++ b/apps/wr/app/store/matDmd/ldrmntCoSttn/headList.tsx
@@ -72,7 +72,7 @@ export const ldrmntHeadListExport: CommonTableProps[] = [
},
{
id: "fctryAddr",
- title: "공장주소",
+ title: "회사주소",
visible: true,
row: 1,
rowSpan: 1,
diff --git a/apps/wr/app/store/matDmd/ldrmntCoSttn/index.tsx b/apps/wr/app/store/matDmd/ldrmntCoSttn/index.tsx
index fed013417..45c6c5773 100644
--- a/apps/wr/app/store/matDmd/ldrmntCoSttn/index.tsx
+++ b/apps/wr/app/store/matDmd/ldrmntCoSttn/index.tsx
@@ -1,6 +1,5 @@
import type { CommonTableProps } from "@/app/store/common/type/header";
import { deleteFectch, fetchType, getFetchWithCount, postFectch, putFectch } from "@/components/lib/api";
-import toast from "react-hot-toast";
import { z } from "zod";
import { TEST_UUID } from "../../ruseFcltMng/fcltReg/rainReg";
import { ldrmntHeadListExport, ldrmntNodeListExport, ldrmntZod } from "./headList";
@@ -35,7 +34,7 @@ type ldrmntActions = {
ldrmntRegData: (data: any[]) => void,
ldrmntInit: (isExcel: boolean, con: string) => void
ldrmntSearch: (isExcel: boolean, page: number, sggCd: string, total: string, coNm: string) => void;
- ldrmntUpdate: (ldrmntRes: customLdrmnt) => void;
+ ldrmntUpdate: (ldrmntRes: customLdrmnt) => Promise;
ldrmntDelete: () => void;
loadingOpen: (isOpen: boolean) => void
};
@@ -115,10 +114,11 @@ export const ldrmntActionsExport: (set: any) => ldrmntActions = (
});
},
ldrmntRegData: async (data) => {
- await deleteFectch(
- `/reuseFc/request/temp`,
- );
- console.log("deleteFectch", deleteFectch)
+ // 잠재수요처 - 삭제 주석 처리
+ // await deleteFectch(
+ // `/reuseFc/request/temp`,
+ // );
+ // console.log("deleteFectch", deleteFectch)
const response = await postFectch(
"/reuseFc/request/temp",
@@ -126,7 +126,8 @@ export const ldrmntActionsExport: (set: any) => ldrmntActions = (
fetchType.WR
);
- console.log('response ', response)
+ console.log('response ', response);
+ return response;
},
ldrmntSearch: async (isExcel: boolean, page: number, total: string, sggCd: string, coNm: string) => {
@@ -192,12 +193,14 @@ export const ldrmntActionsExport: (set: any) => ldrmntActions = (
);
if (res.ok) {
- toast.success("잠재수요처 상세정보가 수정되었습니다.",)
+ return "잠재수요처 상세정보가 수정되었습니다.";
}
+ return "잠재수요처 상세정보 수정에 실패하였습니다.";
} catch (error) {
console.error("Error ldrmntUpdate:", error);
- toast.error("잠재수요처 상세정보 수정에 실패하였습니다.")
+ return "잠재수요처 상세정보 수정에 실패하였습니다.";
}
+ return "잠재수요처 상세정보가 없습니다.";
},
ldrmntDelete: async () => {
try {
@@ -228,4 +231,4 @@ export const ldrmntActionsExport: (set: any) => ldrmntActions = (
}
},
}
-}
\ No newline at end of file
+}
diff --git a/apps/wr/app/store/matDmd/ruseCaseMng/index.tsx b/apps/wr/app/store/matDmd/ruseCaseMng/index.tsx
index 587e08fd9..49cc61d1f 100644
--- a/apps/wr/app/store/matDmd/ruseCaseMng/index.tsx
+++ b/apps/wr/app/store/matDmd/ruseCaseMng/index.tsx
@@ -1,5 +1,4 @@
import { fetchType, getFetchWithCount, postFectch } from "@/components/lib/api";
-import { toast } from "react-hot-toast";
import type { TableUpperProps } from "../../common/type/header";
import { ruseCaseHeadListExport } from "./headList";
import type { ruseCaseMngExcelProps } from "./excel";
@@ -24,7 +23,7 @@ type ruseCaseActions = {
usageCd: string,
name: string
) => Promise;
- insertRuseCaseMng: (data: any) => Promise;
+ insertRuseCaseMng: (data: any) => Promise;
};
const fetchSearchRuseCaseMng = async (
@@ -131,11 +130,10 @@ export const ruseCaseActionsExport: (set: any) => ruseCaseActions = (
}
},
- insertRuseCaseMng: async (data: any) => {
+ insertRuseCaseMng: async (data: any): Promise => {
try {
const response = await fetchInsertRuseCaseMng(data);
if (response.status === 200) {
- toast.success("물재이용사례 등록이 완료되었습니다. ");
const res = await getFetchWithCount(
fetchType.WR,
`/reuseFc/pubFcRuseCase/1/10/0/0`
@@ -147,11 +145,13 @@ export const ruseCaseActionsExport: (set: any) => ruseCaseActions = (
count: res.count,
},
}));
+ return "물재이용사례 등록이 완료되었습니다.";
} else {
+ return "물재이용사례 등록에 실패했습니다.";
}
} catch (e) {
console.log(e);
- toast.error("물재이용사례 등록에 실패하였습니다. ");
+ return "물재이용사례 등록에 실패했습니다.";
}
},
};
diff --git a/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx b/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx
index 186e0bb77..cfb388264 100644
--- a/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx
+++ b/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx
@@ -13,6 +13,7 @@ import {
getInterpolation,
transformAndInterpolate,
} from "@/components/lib/func/calculateConstCost ";
+import { useAlertStore } from "@/src/shared/lib";
import { ConstCostFeeTable } from "@/components/ui/modal/coMat/constCostFeeTable";
import { RateFeeTableModal } from "@/components/ui/modal/coMat/rateFeeModal";
import {
@@ -53,6 +54,7 @@ export default function ConCostForm({
getRateFee,
getConstCostFee,
} = useWrStore((state) => state);
+ const alert = useAlertStore((state) => state.setMessage);
const ref = useRef(false);
@@ -108,11 +110,11 @@ export default function ConCostForm({
const calculateConstCost = useCallback(() => {
const usgPrcsProcs = usgPrcsProcsWatch;
if (!dmndQtyWatch) {
- toast.error("요구수량(m³/일)을 확인해주세요 계산할수 없습니다. ");
+ alert("요구수량(m³/일)을 확인해주세요 계산할수 없습니다. ");
return;
}
if (usgPrcsProcs === null || usgPrcsProcs === undefined) {
- toast.error(
+ alert(
"용도에 따른 처리공정을 확인할수 없습니다. 용도를 확인해주세요. "
);
return;
@@ -175,9 +177,9 @@ export default function ConCostForm({
form.setValue("totalConstCost", Math.round(realResult));
}
}, [constCostRes, rateFeeRes, usgPrcsProcsWatch, dmndQtyWatch]);
- const onSubmit = (value: DmdCoReqType) => {
+ const onSubmit = async (value: DmdCoReqType) => {
if (value.demandCst && !value.demandCst.prcsFcltCpctCstrnCst) {
- toast.error("공사비 계산 진행 후 저장을 눌러주새요");
+ alert("공사비 계산 진행 후 저장을 눌러주새요");
return;
}
@@ -211,10 +213,15 @@ export default function ConCostForm({
{
+ onClick={async () => {
toast.dismiss(t.id);
-
- coMatRvwUpdate(pk, form.getValues("dmdInfoId"), data);
+
+ const message = await coMatRvwUpdate(
+ pk,
+ form.getValues("dmdInfoId"),
+ data
+ );
+ alert(message);
}}
>
확인
@@ -226,10 +233,15 @@ export default function ConCostForm({
));
} else {
- coMatRvwUpdate(pk, form.getValues("dmdInfoId"), data);
+ const message = await coMatRvwUpdate(
+ pk,
+ form.getValues("dmdInfoId"),
+ data
+ );
+ alert(message);
}
}else{
- toast.error('후보지를 먼저 선택해주세요')
+ alert('후보지를 먼저 선택해주세요')
}
}
} else if (type === "hist") {
diff --git a/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx b/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx
index 614a6a7a3..1fffe2cdb 100644
--- a/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx
+++ b/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx
@@ -39,8 +39,9 @@ import type { DemandCompType } from "@common/business_components/ui/sdmInsertFor
import RuseMngTableForm from "@/components/ui/table/pbnstReg/ruseMngTableForm/ruseMngTableForm";
import type { SelectDataType } from "@common/business_components";
import { BuseWaterQulity } from "./buseWaterQulity/buseWaterQulity";
-import toast from "react-hot-toast";
+// import toast from "react-hot-toast";
import { useTranslation } from "react-i18next";
+import { useAlertStore } from "@/src/shared/lib";
//import { useTranslation } from "react-i18next";
@@ -74,6 +75,7 @@ export function PbnstBuseForm({
readonly?: boolean;
}) {
const { t } = useTranslation("base");
+ const alert = useAlertStore((state) => state.setMessage);
const {
code,
@@ -193,7 +195,7 @@ export function PbnstBuseForm({
fcDemands: [],
});
await appendBiz(newItem);
- toast.success(t('insertRuseBiz',{cycle: pbnstBizCycle + 1}))
+ alert(t('insertRuseBiz',{cycle: pbnstBizCycle + 1}))
setIndexBiz(pbnstBizCycle);
}, [form, indexMng, indexBiz, appendBiz, setIndexBiz]);
@@ -779,8 +781,9 @@ export function PbnstBuseForm({
rtnValue={(searchText: string, pageCnt: number) => {
getDemandCompList(searchText, pageCnt);
}}
- rtnInputData={(data) => {
- regDemandCompany(data as DemandCompType);
+ rtnInputData={async (data) => {
+ const message = await regDemandCompany(data as DemandCompType);
+ alert(message);
setIsDmdModal({
isMoal: false,
data: data as DemandCompType,
diff --git a/apps/wr/components/ui/modal/coMat/ruseCaseMng/ruseCaseMngExcelModal.tsx b/apps/wr/components/ui/modal/coMat/ruseCaseMng/ruseCaseMngExcelModal.tsx
index 3d34155e1..78cbd30ec 100644
--- a/apps/wr/components/ui/modal/coMat/ruseCaseMng/ruseCaseMngExcelModal.tsx
+++ b/apps/wr/components/ui/modal/coMat/ruseCaseMng/ruseCaseMngExcelModal.tsx
@@ -2,6 +2,7 @@ import { useWrStore } from "@/app/store";
import { coRegExcel } from "@/app/store/matDmd/coSttn/headList";
import { mapAndValidateRow, type ruseCaseMngExcelProps } from "@/app/store/matDmd/ruseCaseMng/excel";
import { showConfirmToast } from "@/components/lib/func/commom";
+import { useAlertStore } from "@/src/shared/lib";
import { filePicked, Title } from "@common/business_components/ui";
import { Form } from "@common/business_components/ui/form";
import { Button, Input } from "@common/components/ui";
@@ -12,6 +13,7 @@ import type { z } from "zod";
export default function RuseCaseMngExcel({setIsExcelOpen}:{setIsExcelOpen: (isOpen:boolean)=>void}) {
const { insertRuseCaseMng } = useWrStore((state) => state);
+ const alert = useAlertStore((state) => state.setMessage);
const form = useForm>({
resolver: zodResolver(coRegExcel),
});
@@ -20,7 +22,7 @@ export default function RuseCaseMngExcel({setIsExcelOpen}:{setIsExcelOpen: (isOp
const onSubmit = () => {
if (file) {
- filePicked(file, 0).then((res: unknown) => {
+ filePicked(file, 0).then(async (res: unknown) => {
try {
const newArray: ruseCaseMngExcelProps[] = []
const isError:string[][] = []
@@ -144,7 +146,8 @@ export default function RuseCaseMngExcel({setIsExcelOpen}:{setIsExcelOpen: (isOp
alert("업로드할 데이터가 없습니다.");
return;
}else if(newArray.length > 0){
- insertRuseCaseMng(newArray);
+ const message = await insertRuseCaseMng(newArray);
+ alert(message);
setIsExcelOpen(false);
}
} catch (e) {
@@ -188,4 +191,4 @@ export default function RuseCaseMngExcel({setIsExcelOpen}:{setIsExcelOpen: (isOp
>
);
-}
\ No newline at end of file
+}
diff --git a/apps/wr/components/ui/modal/coReg/coRegExcel/coRegExcelModal.tsx b/apps/wr/components/ui/modal/coReg/coRegExcel/coRegExcelModal.tsx
index 1e51fb11c..bc625ec78 100644
--- a/apps/wr/components/ui/modal/coReg/coRegExcel/coRegExcelModal.tsx
+++ b/apps/wr/components/ui/modal/coReg/coRegExcel/coRegExcelModal.tsx
@@ -10,18 +10,20 @@ import { useState } from "react";
import { useForm } from "react-hook-form";
import toast from "react-hot-toast";
import type { z } from "zod";
+import { useConfirm } from "@frontend-opensource/use-react-hooks";
export default function CoRegExcel({
isModal,
}: {
isModal: (open: boolean) => void;
}) {
+ const { confirm } = useConfirm();
const { ldrmnt, loadingOpen } = useWrStore((state) => state);
const { isLoading } = ldrmnt;
const form = useForm>({
resolver: zodResolver(coRegExcel),
});
- const { ldrmntRegData } = useWrStore((state) => state);
+ const { ldrmntRegData, ldrmntDelete } = useWrStore((state) => state);
const [file, setFiles] = useState();
@@ -32,8 +34,7 @@ export default function CoRegExcel({
// }
// };
- //TODO:Onsubmit을 써야하는지
- const onSubmit = () => {
+ const onSubmit = (isReplaceAll = false) => {
loadingOpen(true);
if (file) {
filePicked(file, 0)
@@ -59,10 +60,23 @@ export default function CoRegExcel({
return false;
});
}
+ if (isReplaceAll) {
+ const isConfirmed = await confirm(
+ "기존 등록된 잠재수요처를 \n모두 삭제후 등록처리됩니다. 실행하시겠습니까?",
+ );
+
+ if (!isConfirmed) {
+ loadingOpen(false);
+ return;
+ }
+
+ await ldrmntDelete();
+ }
+
await ldrmntRegData(array);
isModal(false);
loadingOpen(false);
- toast.success(`잠재수요처 등록이 완료되었습니다.`);
+ toast.success(`잠재수요처 등록이 요청되었습니다.`);
} catch (e) {
console.log(e);
loadingOpen(false);
@@ -72,7 +86,10 @@ export default function CoRegExcel({
})
.catch((e) => {
console.log("file", e);
+ loadingOpen(false);
});
+ } else {
+ loadingOpen(false);
}
};
@@ -81,7 +98,7 @@ export default function CoRegExcel({
diff --git a/apps/wr/public/images/map/layericon/BML_CBAS_PS.png b/apps/wr/public/images/map/layericon/BML_CBAS_PS.png
index 0e70939ba..5f98fe6d4 100644
Binary files a/apps/wr/public/images/map/layericon/BML_CBAS_PS.png and b/apps/wr/public/images/map/layericon/BML_CBAS_PS.png differ
diff --git a/apps/wr/public/images/map/layericon/BML_STMS_PS.png b/apps/wr/public/images/map/layericon/BML_STMS_PS.png
index bf4357181..36ba360c2 100644
Binary files a/apps/wr/public/images/map/layericon/BML_STMS_PS.png and b/apps/wr/public/images/map/layericon/BML_STMS_PS.png differ
diff --git a/apps/wr/public/images/map/layericon/RDL_ETCT_PS.png b/apps/wr/public/images/map/layericon/RDL_ETCT_PS.png
index e5a83f998..d52ad75d4 100644
Binary files a/apps/wr/public/images/map/layericon/RDL_ETCT_PS.png and b/apps/wr/public/images/map/layericon/RDL_ETCT_PS.png differ
diff --git a/apps/wr/public/images/map/layericon/RDL_OCUP_PS.png b/apps/wr/public/images/map/layericon/RDL_OCUP_PS.png
index aeae3e266..f3ff30b85 100644
Binary files a/apps/wr/public/images/map/layericon/RDL_OCUP_PS.png and b/apps/wr/public/images/map/layericon/RDL_OCUP_PS.png differ
diff --git a/apps/wr/public/images/map/layericon/RDL_TSCT_PS.png b/apps/wr/public/images/map/layericon/RDL_TSCT_PS.png
index 3776f6c04..81ad25a20 100644
Binary files a/apps/wr/public/images/map/layericon/RDL_TSCT_PS.png and b/apps/wr/public/images/map/layericon/RDL_TSCT_PS.png differ
diff --git a/apps/wr/public/images/map/layericon/RDL_TSCT_PS500.png b/apps/wr/public/images/map/layericon/RDL_TSCT_PS500.png
new file mode 100644
index 000000000..991826693
Binary files /dev/null and b/apps/wr/public/images/map/layericon/RDL_TSCT_PS500.png differ
diff --git a/apps/wr/src/shared/ui/alert-dialog.tsx b/apps/wr/src/shared/ui/alert-dialog.tsx
index 6160d5301..51e82798c 100644
--- a/apps/wr/src/shared/ui/alert-dialog.tsx
+++ b/apps/wr/src/shared/ui/alert-dialog.tsx
@@ -21,10 +21,12 @@ function AlertDialog() {
{message}
-
-
- 확인
-
+
+
+
+ 확인
+
+
diff --git a/apps/wr/src/shared/ui/confirm-dialog.tsx b/apps/wr/src/shared/ui/confirm-dialog.tsx
index 6a4996bfb..7a8065a98 100644
--- a/apps/wr/src/shared/ui/confirm-dialog.tsx
+++ b/apps/wr/src/shared/ui/confirm-dialog.tsx
@@ -28,17 +28,19 @@ function ConfirmDialog({
{message}
-
-
-
- {confirmButtonProps?.label ?? "확인"}
-
-
-
-
- {cancelButtonProps?.label ?? "취소"}
-
-
+
+
+
+
+ {confirmButtonProps?.label ?? "확인"}
+
+
+
+
+ {cancelButtonProps?.label ?? "취소"}
+
+
+
diff --git a/apps/wr/tsconfig.json b/apps/wr/tsconfig.json
index 493584782..221de4580 100644
--- a/apps/wr/tsconfig.json
+++ b/apps/wr/tsconfig.json
@@ -28,13 +28,9 @@
"@/*": ["./*"],
"@common/components/*": ["../../packages/common/components/src/*"],
"@common/components": ["../../packages/common/components/src/index"],
- "@common/business_components/*": [
- "../../packages/common/business_components/src/*"
- ],
- "@common/business_components": [
- "../../packages/common/business_components/src/index"
- ],
- "@common/network": ["../../packages/common/network"]
+ "@common/business_components/*": ["../../packages/common/business_components/src/*"],
+ "@common/business_components": ["../../packages/common/business_components/src/index"],
+ "@common/network": ["../../packages/common/network"],
}
},
"include": [
diff --git a/ecosystem.config.js b/ecosystem.config.js
index 8c5873da0..497e040bc 100644
--- a/ecosystem.config.js
+++ b/ecosystem.config.js
@@ -1,6 +1,6 @@
const common = {
exec_mode: 'cluster',
- instances: 'max',
+ instances: 2,
autorestart: true,
max_memory_restart: '1G',
diff --git a/packages/common/business_components/src/ui/map/Gis.js b/packages/common/business_components/src/ui/map/Gis.js
index 8a33b1123..249c94a13 100644
--- a/packages/common/business_components/src/ui/map/Gis.js
+++ b/packages/common/business_components/src/ui/map/Gis.js
@@ -19,6 +19,7 @@ import GisMapToggle from "./GisMapToggle";
import GisSelect from "./GisSelect";
import GisToolBar from "./GisToolBar";
import GisTree from "./GisTree";
+import GisZoomLevel from "./GisZoomLevel";
import "./lib/css/gis.common.css";
import GisApp from "./lib/gis.core";
import LoadingOverlay from "@common/business_components/ui/loadingBar";
@@ -58,6 +59,7 @@ function Gis({
droughtDates = { startYmd: '', endYmd: '' },
defaultZoom = 15,
defaultLonLat = [127.4875, 37.4916],
+ showZoomLevel = false,
}) {
const mapRef = useRef();
const [mapObject, setMapObject] = useState({});
@@ -325,6 +327,7 @@ function Gis({
)}
+ {showZoomLevel &&
}
diff --git a/packages/common/business_components/src/ui/map/GisZoomLevel.js b/packages/common/business_components/src/ui/map/GisZoomLevel.js
new file mode 100644
index 000000000..34d74f252
--- /dev/null
+++ b/packages/common/business_components/src/ui/map/GisZoomLevel.js
@@ -0,0 +1,113 @@
+import React, { useEffect, useRef, useState } from 'react';
+import { useGisContext } from './Gis';
+
+export default React.memo(function GisZoomLevel() {
+ const { gisMap } = useGisContext();
+ const trackRef = useRef(null);
+ const [zoomInfo, setZoomInfo] = useState({
+ zoom: 0,
+ minZoom: 0,
+ maxZoom: 0,
+ });
+
+ useEffect(() => {
+ if (!gisMap) return undefined;
+
+ const view = gisMap.getView();
+ if (!view) return undefined;
+
+ const updateZoomInfo = () => {
+ setZoomInfo({
+ zoom: view.getZoom() ?? 0,
+ minZoom: view.getMinZoom?.() ?? 0,
+ maxZoom: view.getMaxZoom?.() ?? 20,
+ });
+ };
+
+ updateZoomInfo();
+ view.on('change:resolution', updateZoomInfo);
+
+ return () => {
+ view.un('change:resolution', updateZoomInfo);
+ };
+ }, [gisMap]);
+
+ if (!gisMap) return <>>;
+
+ const { zoom, minZoom, maxZoom } = zoomInfo;
+ const range = Math.max(maxZoom - minZoom, 1);
+ const normalized = Math.min(Math.max((zoom - minZoom) / range, 0), 1);
+ const fillHeight = `${normalized * 100}%`;
+ const thumbBottom = `calc(${fillHeight} - 1px)`;
+
+ const updateZoomFromClientY = clientY => {
+ if (!gisMap || !trackRef.current) return;
+
+ const view = gisMap.getView();
+ const rect = trackRef.current.getBoundingClientRect();
+ const offsetY = rect.bottom - clientY;
+ const nextNormalized = Math.min(Math.max(offsetY / rect.height, 0), 1);
+ const nextZoom = minZoom + nextNormalized * range;
+ view.setZoom(nextZoom);
+ };
+
+ const zoomIn = () => {
+ const view = gisMap.getView();
+ if (view.getMaxZoom() > view.getZoom()) {
+ view.animate({
+ zoom: view.getZoom() + 1,
+ duration: 250,
+ });
+ }
+ };
+
+ const zoomOut = () => {
+ const view = gisMap.getView();
+ if (view.getMinZoom() < view.getZoom()) {
+ view.animate({
+ zoom: view.getZoom() - 1,
+ duration: 250,
+ });
+ }
+ };
+
+ const handleTrackPointerDown = event => {
+ event.preventDefault();
+ updateZoomFromClientY(event.clientY);
+
+ const handlePointerMove = moveEvent => {
+ updateZoomFromClientY(moveEvent.clientY);
+ };
+
+ const handlePointerUp = () => {
+ window.removeEventListener('pointermove', handlePointerMove);
+ window.removeEventListener('pointerup', handlePointerUp);
+ };
+
+ window.addEventListener('pointermove', handlePointerMove);
+ window.addEventListener('pointerup', handlePointerUp);
+ };
+
+ return (
+
+ );
+});
diff --git a/packages/common/business_components/src/ui/map/lib/css/gis.common.css b/packages/common/business_components/src/ui/map/lib/css/gis.common.css
index f9b67a07b..5ce46a912 100644
--- a/packages/common/business_components/src/ui/map/lib/css/gis.common.css
+++ b/packages/common/business_components/src/ui/map/lib/css/gis.common.css
@@ -173,6 +173,100 @@
text-indent: -9999px;
}
+.zoom-level-control {
+ position: absolute;
+ right: 20px;
+ bottom: 60px;
+ z-index: 2;
+}
+
+.zoom-level-panel {
+ overflow: hidden;
+ width: 40px;
+ background: #fff;
+ border: 1px solid #9aa7b8;
+ border-radius: 3px;
+ box-shadow: 0px 0px 5px #999;
+ box-sizing: border-box;
+}
+
+.zoom-level-btn {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 40px;
+ height: 30px;
+ padding: 0;
+ border: 0;
+ border-bottom: 1px solid #eeeeee;
+ background: #fff;
+ color: #333;
+ font-size: 26px;
+ line-height: 1;
+ cursor: pointer;
+}
+
+.zoom-level-panel .zoom-level-btn:last-child {
+ border-top: 1px solid #eeeeee;
+ border-bottom: 0 none;
+}
+
+.zoom-level-btn:hover {
+ background: #67aee3;
+ color: #fff;
+}
+
+.zoom-level-gauge {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 40px;
+ height: 128px;
+ background: #fff;
+}
+
+.zoom-level-track {
+ position: relative;
+ width: 12px;
+ height: 96px;
+ border-radius: 6px;
+ cursor: ns-resize;
+ touch-action: none;
+ background:
+ linear-gradient(to top, #b8b8b8, #b8b8b8) center / 4px 100% no-repeat,
+ repeating-linear-gradient(
+ to top,
+ transparent 0,
+ transparent 8px,
+ #d9d9d9 8px,
+ #d9d9d9 9px
+ ),
+ linear-gradient(to top, #f5f5f5, #fafafa);
+}
+
+.zoom-level-fill {
+ position: absolute;
+ left: 50%;
+ bottom: 0;
+ width: 4px;
+ background: #3b82f6;
+ border-radius: 999px;
+ transform: translateX(-50%);
+}
+
+.zoom-level-thumb {
+ position: absolute;
+ left: 50%;
+ width: 22px;
+ height: 5px;
+ border: 0.5px solid #111;
+ border-radius: 2px;
+ background: #fff;
+ transform: translateX(-50%);
+ box-sizing: border-box;
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);
+}
+
.ol-scale-line {
background: rgba(0, 60, 136, 0.3);
border-radius: 4px;
diff --git a/packages/common/business_components/src/ui/map/lib/gis.layer.js b/packages/common/business_components/src/ui/map/lib/gis.layer.js
index 1e8bb428c..314b93338 100644
--- a/packages/common/business_components/src/ui/map/lib/gis.layer.js
+++ b/packages/common/business_components/src/ui/map/lib/gis.layer.js
@@ -118,7 +118,7 @@ GisOlLayer.prototype = {
let toProj = GisApp.TO_PROJ;
let lyrTyNm = options.lyrTyNm;
let typename = this.storageNm + lyrTyNm;
- let filter = options?.flterSql && options.flterSql && options.flterSql.trim() !== '';
+ const filter = (options?.flterSql ?? '').trim();
let params = {};
params['TYPENAME'] = typename;