diff --git a/apps/dm/app/(base-layout)/pages/sdmMng/sdmApplyList/page.tsx b/apps/dm/app/(base-layout)/pages/sdmMng/sdmApplyList/page.tsx index 9c2f6201e..8c681876c 100644 --- a/apps/dm/app/(base-layout)/pages/sdmMng/sdmApplyList/page.tsx +++ b/apps/dm/app/(base-layout)/pages/sdmMng/sdmApplyList/page.tsx @@ -60,6 +60,8 @@ export default function SdmApplyList({}: SdmApplyProps) { // 데이터 변환을 위한 별도의 useEffect useEffect(() => { if (sdmInfoStatusList.length > 0) { + + const newTransformedList = sdmInfoStatusList.map((item: DemandCompBizType) => ({ ...item, ctpvCd: selectSidoData.find(sido => sido.val === item.ctpvCd?.trim())?.text || '', 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 8e5194bcd..b6a395974 100644 --- a/apps/dm/app/(base-layout)/pages/sdmMng/sdmInfoInsert/page.tsx +++ b/apps/dm/app/(base-layout)/pages/sdmMng/sdmInfoInsert/page.tsx @@ -54,11 +54,25 @@ function SdmInfoInsertContent() { useEffect(() => { const user = sessionStorage.getItem("user"); + + setDefaultValues((prev) => ({ ...prev, sdmNm: searchParams.get("dmdCoNm") || "", })); - sessionStorage.setItem("user", searchParams.get("dmdInfoId") || ""); + if(searchParams.get("dmdInfoId")){ + // const data = { + // dmdCoNm: searchParams.get("dmdCoNm") || '', + // ctpvCd: searchParams.get("ctpvCd") || '', + // sggCd: searchParams.get("sggCd") || '', + // dmdInfoId: searchParams.get("dmdInfoId") || '', + // daddr: searchParams.get("daddr") || '', + // telno: searchParams.get("telno") || '', + // } + + // sessionStorage.setItem("user", JSON.stringify(data || {})); + } + //sessionStorage.setItem("user", JSON.parse(user||'') || ""); setIsLogin(user || searchParams.get("dmdInfoId") ? true : false); }, [searchParams]); diff --git a/apps/dm/app/store/sdmMng/index.tsx b/apps/dm/app/store/sdmMng/index.tsx index 1b63c399d..35dd3d2a9 100644 --- a/apps/dm/app/store/sdmMng/index.tsx +++ b/apps/dm/app/store/sdmMng/index.tsx @@ -691,6 +691,7 @@ export const SdmReducer = (set: any): SdmActions => { // 수요정보 신청현황 getSdmInfoStatusList: async (dmdInfoId: string) => { const res = await getSdmInfoStatusList(dmdInfoId); + if(res.length === 0) { set((state: SdmType) => ({ Sdm: { @@ -700,15 +701,15 @@ export const SdmReducer = (set: any): SdmActions => { })) return; } - 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 : ''; - const tmpData = { + return( { ...el, index: ++index, bzmnNo: res.bzmnNo, @@ -725,9 +726,9 @@ export const SdmReducer = (set: any): SdmActions => { status : status, splyCndtList: el.splyCndtList, ruseUtztnFcltNm: ruseUtztnFcltNm || '' - } - sdmInfoStatusList.push(tmpData); + }) }) + set((state: SdmType) => ({ Sdm: { ...state.Sdm, diff --git a/apps/sn/.env.production b/apps/sn/.env.production index 805498398..7d7a547eb 100644 --- a/apps/sn/.env.production +++ b/apps/sn/.env.production @@ -10,4 +10,4 @@ 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_DC_URL = http://apigateway.iipbs-dev.svc.cluster.local:8000 NEXT_PUBLIC_NGII_URL = http://10.98.25.94:18080/openapi/Gettile.do?apiKey=BDE2BEF39DAFEC293AB6730150F222AC -NEXT_PUBLIC_SEWERAGE_API_URL = http://apigateway.iipbs-dev.svc.cluster.local:8000/sewerage-service/api +NEXT_PUBLIC_SEWERAGE_API_URL = https://apigateway.hasuon.or.kr/sewerage-service/api diff --git a/apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx b/apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx index 8b38d232d..3ec23a850 100644 --- a/apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx +++ b/apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx @@ -149,18 +149,7 @@ const SearchModal: React.FC = ({ setIsResetDetailSelection(true); // 선택 초기화 setDetailBizId(0); // queryParams는 fetchFilteredData 내에서 매번 새로 생성되므로 별도 상태 초기화는 필요 없음 - }, [activeTab, basicType, workType, open]); - - useEffect(() => { - setActiveTab(TABS[0]?.val ?? "basic"); - setBasicType(BASIC_TYPES[0]?.val ?? ""); - setWorkType(WORK_TYPES[0]?.val ?? ""); - setTableCount(0); - setTableData([]); - setDetailTableCount(0); - setDetailTableData([]); - setDetailBizId(0); - }, [open]); + }, [activeTab, basicType, workType]); // 페이지 변경 시마다 데이터 fetch useEffect(() => { @@ -490,14 +479,16 @@ const SearchModal: React.FC = ({ }; // 기본정보 테이블 cellClick 핸들러 - const handleBasicCellClick = (index?: number | undefined, headId?: String) => { + const handleBasicCellClick = async (index?: number | undefined, headId?: String) => { if (typeof index === "number") { + setIsLoading(true); const row = tableData[index]; const layerNm = row.lyrTypeNm; const fid = row.fid; const core = GisApp.Module.layer as any; - core.showSearchResultsFromGeoJSON(layerNm, fid); + await core.showSearchResultsFromGeoJSON(layerNm, fid); + setIsLoading(false); onOpenChange(false); // SearchModal 닫기 } }; @@ -521,8 +512,7 @@ const SearchModal: React.FC = ({ RequestDTO.workTypes = JSON.parse(workType.replace(/'/g, '"')); const res = await getData(`v1/bizInfo/fclts?${new URLSearchParams(RequestDTO).toString()}`); - console.log('운영정보 cellClick 데이터1:', res); - console.log('운영정보 cellClick 데이터2:', res.data); + console.log('운영정보 cellClick 데이터:', res.data); const mappedData = (res.data || []).map((item: any, detailIndex: number) => { const formattedItem = formatDisplayData(item); @@ -587,8 +577,22 @@ const SearchModal: React.FC = ({ } }; + const handleOperationResultCellClick = async (index?: number | undefined, headId?: String) => { + if (typeof index === "number") { + setIsLoading(true); + + const row = detailTableData[index]; + const layerNm = row.lyrTypeNm; + const fid = row.fid; + const core = GisApp.Module.layer as any; + await core.showSearchResultsFromGeoJSON(layerNm, fid); + setIsLoading(false); + onOpenChange(false); // SearchModal 닫기 + } + }; + // 기본정보 Excel 다운로드 핸들러 - const BasicExcelDownload = async () => { + const basicExcelDownload = async () => { if (tableData.length === 0) { alert("다운로드 할 Excel 파일이 없습니다.\n조회 후 다시 시도해주세요."); return; @@ -804,7 +808,7 @@ const SearchModal: React.FC = ({ }; // 운영정보 Excel 다운로드 핸들러 추가 - const OperationExcelDownload = async () => { + const operationExcelDownload = async () => { if (tableData.length === 0) { alert("다운로드 할 Excel 파일이 없습니다.\n조회 후 다시 시도해주세요."); return; @@ -898,7 +902,7 @@ const SearchModal: React.FC = ({ }; // 운영정보 Excel 다운로드 핸들러 추가 - const OperationResultExcelDownload = async () => { + const operationResultExcelDownload = async () => { if (detailTableData.length === 0) { alert("다운로드 할 Excel 파일이 없습니다.\n조회 후 다시 시도해주세요."); return; @@ -1001,12 +1005,41 @@ const SearchModal: React.FC = ({ } }; + const viewMultipleFeature = async () => { + if (activeTab === 'basic') { + if (!(tableData.length === 0)) { + setIsLoading(true); + + const row = tableData[0]; + const layerNm = row.lyrTypeNm; + const fids = tableData.map((item) => item.fid); + const core = GisApp.Module.layer as any; + await core.showMultipleSearchResultsFromGeoJSON(layerNm, fids); + setIsLoading(false); + onOpenChange(false); + } + } else if (activeTab === 'operation') { + if (!(detailTableData.length === 0)) { + setIsLoading(true); + + const row = detailTableData[0]; + console.log('row', row); + const layerNm = row.lyrTypeNm; + const fids = detailTableData.map((item) => item.fid); + const core = GisApp.Module.layer as any; + await core.showMultipleSearchResultsFromGeoJSON(layerNm, fids); + setIsLoading(false); + onOpenChange(false); + } + } + } + const renderBody = () => ( <> {/* 탭 영역 */} ({ value: tab.val, label: tab.text @@ -1084,7 +1117,10 @@ const SearchModal: React.FC = ({ resetPageNo={isResetPage} resetSelection={isResetSelection} headButton={ - +
+ + +
} /> @@ -1112,7 +1148,7 @@ const SearchModal: React.FC = ({ resetPageNo={isResetPage} resetSelection={isResetSelection} headButton={ - + } /> @@ -1133,12 +1169,15 @@ const SearchModal: React.FC = ({ headlist={dynamicHeadlist2} pageSize={OPERATION_TABLE_SIZE} total={detailTableCount} - cellClick={undefined} + cellClick={handleOperationResultCellClick} onPageChange={setDetailCurPage} resetPageNo={isResetDetailPage} resetSelection={isResetDetailSelection} headButton={ - +
+ + +
} /> diff --git a/apps/sn/app/(with-base-layout)/ii/BaseLayout.tsx b/apps/sn/app/(with-base-layout)/ii/BaseLayout.tsx index bf056f4e7..af178ac59 100644 --- a/apps/sn/app/(with-base-layout)/ii/BaseLayout.tsx +++ b/apps/sn/app/(with-base-layout)/ii/BaseLayout.tsx @@ -58,6 +58,8 @@ type BaseLayout = { onDeleteFile?: () => void; onAnalysis?: () => void; selectedRowData?: any; + isIr?: boolean; + isRdii?: boolean; } const BaseLayout = ({ @@ -87,6 +89,8 @@ const BaseLayout = ({ onExcelDownload, onDeleteFile, onAnalysis, + isIr = false, + isRdii = false, }: BaseLayout) => { // 컴포넌트 언마운트 시 confirmToast 정리 @@ -207,6 +211,24 @@ const BaseLayout = ({ {graphBtn && ( )} + {/* 침입수 분석 */} + {isIr && ( + + )} + {/* RDII 분석 */} + {isRdii && ( + + )} {/* 분석 버튼 */} {analysisBtn && ( diff --git a/apps/sn/app/(with-base-layout)/ii/fileupload/page.tsx b/apps/sn/app/(with-base-layout)/ii/fileupload/page.tsx new file mode 100644 index 000000000..e72f7f4a5 --- /dev/null +++ b/apps/sn/app/(with-base-layout)/ii/fileupload/page.tsx @@ -0,0 +1,99 @@ +'use client' + +// FileUpload.js +import React, { useState } from 'react'; +import axios from 'axios'; +const SEWERAGE_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL || ""; + +function FileUpload() { + const [file, setFile] = useState(null); + const [text, setText] = useState(''); + + const handleFileChange = (e) => { + setFile(e.target.files[0]); + }; + + const handleTextChange = (e) => { + setText(e.target.value); + }; + + function getCookie(name:string) { + const match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)')); + if (match && match[2]) { + return decodeURIComponent(match[2]); + } + return null; + } + const handleFindUser = (e) => { + const auth = getCookie("Authorization"); + if (auth) console.log("쿠키 auth : " + auth); + fetch(SEWERAGE_URL+"/v1/inftwtAnls/users/65a00f65-8460-49af-98ec-042977e56f4b", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({authToken: auth}) + }).then((data) => { + console.log(data); + }); + }; + const handleFindUserOna = (e) => { + const auth = getCookie("Authorization"); + if (auth) console.log("쿠키 auth : " + auth); + fetch("/server/user-service/api/v1/users/65a00f65-8460-49af-98ec-042977e56f4b").then((data) => { + console.log(data); + }); + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + + if (!file) { + alert('파일을 선택하세요.'); + return; + } + + const jsonData = { + aplcnYm: '202507', + enoCd: 'ABC' + }; + + // JSON → Blob 으로 변환 + MIME 지정 + const jsonBlob = new Blob([JSON.stringify(jsonData)], { + type: 'application/json' + }); + + const formData = new FormData(); + formData.append('data', jsonBlob); + formData.append('inpFile', file); + + try { + const response = await axios.post('http://localhost:8883/sewerage-service/api/v1/rdiiAnls/savewithfile', formData); + + alert('업로드 성공: ' + response.data.message); + } catch (err) { + console.error(err); + alert('업로드 실패'); + } + }; + + return ( +
+
+ + +
+
+ + +
+ + + +
+ ); +} + +export default FileUpload; diff --git a/apps/sn/app/(with-base-layout)/ii/irsetting/page.tsx b/apps/sn/app/(with-base-layout)/ii/irsetting/page.tsx index 6af066e4d..97864aa20 100644 --- a/apps/sn/app/(with-base-layout)/ii/irsetting/page.tsx +++ b/apps/sn/app/(with-base-layout)/ii/irsetting/page.tsx @@ -1,5 +1,5 @@ 'use client' -import { postData } from '@/components/lib/api' +import { postMutiPart } from '@/components/lib/api' import FileUploadForm from '@/components/ui/fileUploadForm' import FormSection from '@/components/ui/formSection' import { DateMonthPicker, filePicked } from '@common/business_components/ui' @@ -52,6 +52,19 @@ const IrSetting = () => { const [sggCd, setSggCd] = useState('0'); // 시군구 코드 상태 const [sggName, setSggName] = useState(''); // 시군구 이름 상태 const [swtrmtFcltNm, setSwtrmtFcltNm] = useState(''); // 하수처리시설명 상태 + const [uploadedFilesData, setUploadedFilesData] = useState([ + initialFileInfo, // 0: 오수관망도 + initialFileInfo, // 1: 수용가-맨홀 매칭결과 + initialFileInfo, // 2: 오수량 시간패턴 + initialFileInfo // 3: 유량자료 등록 + ]); + // selectedDate를 yyyyMM 문자열 형식으로 변경 + const [selectedDate, setSelectedDate] = useState(() => { + const now = new Date(); + const year = now.getFullYear(); + const month = String(now.getMonth() + 1).padStart(2, '0'); + return `${year}${month}`; + }); const { data: userInfo } = useQuery({ queryKey: [ENDPOINT.USER_SERVICE.USERS], @@ -72,21 +85,6 @@ const IrSetting = () => { } }) - const [uploadedFilesData, setUploadedFilesData] = useState([ - initialFileInfo, // 0: 오수관망도 - initialFileInfo, // 1: 수용가-맨홀 매칭결과 - initialFileInfo, // 2: 오수량 시간패턴 - initialFileInfo // 3: 유량자료 등록 - ]); - - // selectedDate를 yyyyMM 문자열 형식으로 변경 - const [selectedDate, setSelectedDate] = useState(() => { - const now = new Date(); - const year = now.getFullYear(); - const month = String(now.getMonth() + 1).padStart(2, '0'); - return `${year}${month}`; - }); - // 폼 초기화 함수 const resetAllForms = () => { form.reset({ // react-hook-form 초기화 @@ -121,7 +119,7 @@ const IrSetting = () => { index: number, // FileUploadForm의 fileIdx 값 selectedFile: File | null, cd: string | null, - id: string | null + id: string | null, ) => { setUploadedFilesData(prevData => { const newData = [...prevData]; @@ -137,17 +135,16 @@ const IrSetting = () => { const registFile = async (analysisData: any) => { - setIsLoadingResult(true); // 로딩 상태 시작 + setIsLoadingResult(true); console.log("Submitting combined analysis data to backend:", analysisData); - // 추후 API 엔드포인트에 맞게 URL 수정 필요 - const data = await postData('v1/inftwtAnls', analysisData); + const data = await postMutiPart('v1/inftwtAnls/savewithfile', analysisData); console.log("Response from backend:", data); if (data) { - setIsLoadingResult(false); // 로딩 상태 시작 + setIsLoadingResult(false); alert("분석 데이터가 성공적으로 제출되었습니다."); resetAllForms(); // 성공 시 폼 초기화 함수 호출 } else { - setIsLoadingResult(false); // 로딩 상태 시작 + setIsLoadingResult(false); alert("분석 데이터 제출 중 오류가 발생했습니다."); } }; @@ -306,10 +303,10 @@ const IrSetting = () => { : null; const rcptrInfoData = file5?.file ? parsedResults.find(r => r && r.fileName === file5.file?.name)?.parsedData - : null; + : null; // 새로운 JSON 데이터 생성 - const combinedAnalysisData = { + const jsonData = { ctpvCd: ctpvCd !== '0' ? String(ctpvCd).trim() : null, sggCd: sggCd !== '0' ? String(sggCd) : null, // 시군구코드 (선택값) swtrmtFcltNm: swtrmtFcltNm ?? null, @@ -318,8 +315,8 @@ const IrSetting = () => { anlsInfo: input2, // 분석정보 rgtr: tokenManager.getToken(AUTH_USER_ID) || null, // 등록자 (사용자 ID) rgtrNm: userInfo?.userName, // 등록자 (사용자 ID) - atflCd: file1?.atflCd, // 첨부파일CD (오수관망도) - atflId: file1?.atflId, // 첨부파일ID (오수관망도) + //atflCd: file1?.atflCd, // 첨부파일CD (오수관망도) + //atflId: file1?.atflId, // 첨부파일ID (오수관망도) atflInpNm: file1?.file?.name, // 오수관망도 파일명 atflHrPttnNm: file3?.file?.name, // 오수량 시간패턴 파일명 atflMpngNm: file2?.file?.name, // 수용가-맨홀 매칭결과 파일명 @@ -330,9 +327,15 @@ const IrSetting = () => { tbSnRcptrInfo: rcptrInfoData, // 수용가 상수사용량 (없을 수 있음) }; - console.log("통합 분석 데이터 (JSON):", combinedAnalysisData); + console.log("통합 분석 데이터 (JSON):", jsonData); + + const jsonBlob = new Blob([JSON.stringify(jsonData)], { + type: 'application/json' + }); + const combinedAnalysisData = new FormData(); + combinedAnalysisData.append('data', jsonBlob); + if (file1?.file) combinedAnalysisData.append('inpFile', file1.file); - // 수정된 registFile 함수를 호출하여 combinedAnalysisData를 전송합니다. await registFile(combinedAnalysisData); setIsLoadingResult(false); } catch (error) { @@ -342,9 +345,8 @@ const IrSetting = () => { return; } } - // const handleAnalysisClick = () => { - // } - //const rcptrInfoShow = true; + + return ( <> @@ -366,36 +368,10 @@ const IrSetting = () => { setSwtrmtFcltNm={setSwtrmtFcltNm} setIsLoading={setIsLoadingResult} onAnalysis={handleAnalysisClick} + isIr={true} > {[
- - handleFileSelection(0, file, cd, id)} - showButton={false} - showPreview={true} - accept={".inp"} - fileIdx={0} /> - - - handleFileSelection(1, file, cd, id)} - showButton={false} - showPreview={true} - accept={".xls, .xlsx, .xlsm, .xltx"} - fileIdx={1} /> - - - handleFileSelection(2, file, cd, id)} - showButton={false} - showPreview={true} - accept={".xls, .xlsx, .xlsm, .xltx"} - fileIdx={2} /> - - + {/* 연월 선택 라이브러리로 수정 필요 */} { accept={".xls, .xlsx, .xlsm, .xltx"} fileIdx={4} /> - + + handleFileSelection(0, file, cd, id)} + showButton={false} + showPreview={true} + accept={".inp"} + fileIdx={0} /> + + +
+ handleFileSelection(1, file, cd, id)} + showButton={false} + showPreview={true} + accept={".xls, .xlsx, .xlsm, .xltx"} + fileIdx={1} /> + {/* */} +
+
+ + handleFileSelection(2, file, cd, id)} + showButton={false} + showPreview={true} + accept={".xls, .xlsx, .xlsm, .xltx"} + fileIdx={2} /> + + { @@ -445,7 +456,7 @@ const IrSetting = () => { placeholder="예: 0.75" /> - + setInput2(e.target.value)} diff --git a/apps/sn/app/(with-base-layout)/ii/rdii/page.tsx b/apps/sn/app/(with-base-layout)/ii/rdii/page.tsx index 7354db6cd..8a27abb81 100644 --- a/apps/sn/app/(with-base-layout)/ii/rdii/page.tsx +++ b/apps/sn/app/(with-base-layout)/ii/rdii/page.tsx @@ -1,6 +1,6 @@ "use client" -import { postData } from '@/components/lib/api' +import { postMutiPart } from '@/components/lib/api' import FileUploadForm from "@/components/ui/fileUploadForm" import FormSection from "@/components/ui/formSection" import BaseLayout from "../BaseLayout" @@ -157,13 +157,18 @@ export default function RTKForm() { }; const registFile = async (analysisData: any) => { - console.log("Submitting combined analysis data to backend"); - // 추후 API 엔드포인트에 맞게 URL 수정 필요 - const data = await postData('v1/rdiiAnls', analysisData); + setIsLoadingResult(true); + console.log("Submitting combined analysis data to backend", analysisData); + const data = await postMutiPart('v1/rdiiAnls/savewithfile', analysisData); + console.log("Response from backend:", data); if (data) { + setIsLoadingResult(false); alert("분석 데이터가 성공적으로 제출되었습니다."); resetAllForms(); // 성공 시 폼 초기화 함수 호출 - } else alert("분석 데이터 제출 중 오류가 발생했습니다."); + } else { + setIsLoadingResult(false); + alert("분석 데이터 제출 중 오류가 발생했습니다."); + } }; // 침입수 분석 실행 버튼 클릭 핸들러 @@ -182,7 +187,7 @@ export default function RTKForm() { } const formData = form.getValues(); - console.log(uploadedFilesData); + console.log('uploadedFilesData', uploadedFilesData); const file1 = uploadedFilesData[0]; // 오수관망도 const file2 = uploadedFilesData[1]; // 수용가-맨홀 매칭결과 const file3 = uploadedFilesData[2]; // 강우자료 @@ -253,9 +258,9 @@ export default function RTKForm() { if (!file || !file.file) return null; // fileInfo 객체 및 file 객체 null 체크 // filePicked 함수에 File 객체와 headerNum 전달 - const jsonData = await filePicked(file.file, 0); + const ExcelToJsonData = await filePicked(file.file, 0); - if (jsonData === undefined) { + if (ExcelToJsonData === undefined) { console.warn(`Warning: filePicked returned undefined for ${file.file.name}`); return { fileName: file.file.name, @@ -265,9 +270,9 @@ export default function RTKForm() { }; } - // jsonData를 각 parser 함수로 처리 + // ExcelToJsonData를 각 parser 함수로 처리 // parser 함수가 filePicked가 반환하는 JSON 형식을 직접 처리한다고 가정 - const parsedDataBySpecificFunction = parser(jsonData); + const parsedDataBySpecificFunction = parser(ExcelToJsonData); return { fileName: file.file.name, @@ -279,11 +284,28 @@ export default function RTKForm() { ); // 각 파싱 결과 접근 - const matchingResultData = parsedResults.find(r => r && r.fileName === file2.file?.name)?.parsedData; - const rainData = parsedResults.find(r => r && r.fileName === file3.file?.name)?.parsedData; + const matchingResultData = parsedResults.find(r => r && r.fileName === file2.file?.name)?.parsedData ?? []; + const rainData = parsedResults.find(r => r && r.fileName === file3.file?.name)?.parsedData ?? []; + + // 상수사용량 적용월과 강우자료의 날짜 일치 여부 검증 + let dateValidated = true; + + if (rainData) { + rainData + .filter(data => data && 'mtrngDt' in data) + .map(data => { + if (formData.date !== data.mtrngDt.replace(/-/g, '').slice(0, 6)) dateValidated = false; + }); + } + if (!dateValidated) { + setIsLoadingResult(false); + alert("'상수사용량 적용월'과 강우자료의 '날짜'가 일치하지 않습니다."); + return; + } + // 새로운 JSON 데이터 생성 - const combinedAnalysisData = { + const jsonData = { aplcnYm: formData.date, // 적용연월 (YYYYMM 형식) ctpvCd: ctpvCd !== '0' ? String(ctpvCd).trim() : null, // 시도코드 (선택값) sggCd: sggCd !== '0' ? String(sggCd) : null, // 시군구코드 (선택값) @@ -329,9 +351,15 @@ export default function RTKForm() { tbSnRainData: rainData, // 강우자료 }; - console.log("통합 분석 데이터 (JSON):", combinedAnalysisData); + console.log("통합 분석 데이터 (JSON):", jsonData); + + const jsonBlob = new Blob([JSON.stringify(jsonData)], { + type: 'application/json' + }); + const combinedAnalysisData = new FormData(); + combinedAnalysisData.append('data', jsonBlob); + if (file1?.file) combinedAnalysisData.append('inpFile', file1.file); - // 수정된 registFile 함수를 호출하여 combinedAnalysisData를 전송합니다. await registFile(combinedAnalysisData); setIsLoadingResult(false); } catch (error) { @@ -389,18 +417,20 @@ export default function RTKForm() { setSwtrmtFcltNm={setSwtrmtFcltNm} setIsLoading={setIsLoadingResult} onAnalysis={handleAnalysisClick} + isRdii={true} > {[
- + - +
+ *
+ *
-
+
handleFileSelection(0, file, cd, id)} @@ -430,7 +461,7 @@ export default function RTKForm() { accept={".inp"} fileIdx={0} />
-
+
handleFileSelection(1, file, cd, id)} @@ -438,8 +469,14 @@ export default function RTKForm() { showPreview={true} accept={".xls, .xlsx, .xlsm, .xltx"} fileIdx={1} /> + {/* */}
-
+
handleFileSelection(2, file, cd, id)} @@ -448,7 +485,7 @@ export default function RTKForm() { accept={".xls, .xlsx, .xlsm, .xltx"} fileIdx={2} />
- + { @@ -478,7 +515,7 @@ export default function RTKForm() { placeholder="0 ~ 1 사이의 값 입력" /> - + - + { @@ -32,7 +33,8 @@ const Page = () => { const [curPage, setCurPage] = useState(1) //현재 페이지 const [totalData, setTotalData] = useState(0) //총 데이터 수 const [isLoading, setIsLoading] = useState(true) //로딩 상태 - const [isModalOpen, setIsModalOpen] = useState(false) // 모달 상태 + const [isUploadModalOpen, setIsUploadModalOpen] = useState(false) // 등록 모달 상태 + const [isTableModalOpen, setIsTableModalOpen] = useState(false) // 테이블 모달 상태 const [resetSelection, setResetSelection] = useState(false); // 선택 초기화 상태 const [isResetPage, setIsResetPage] = useState(false); @@ -135,14 +137,14 @@ const Page = () => { const data = await postData('v1/sbmrgPrevnt', json) if (data) { alert('파일이 등록되었습니다.'); - setIsModalOpen(false); + setIsUploadModalOpen(false); handleSearch(); } } // 등록 버튼 클릭 핸들러 const handleRegisterClick = () => { - setIsModalOpen(true); + setIsUploadModalOpen(true); } @@ -211,7 +213,7 @@ const Page = () => {

사업정보 등록현황

- +
@@ -228,6 +230,7 @@ const Page = () => { setFileNm(fileName); setAtflId(id); setSbmrgPrevntBizSn(tableData[index]?.sbmrgPrevntBizSn); + setIsTableModalOpen(true); } }} onPageChange={setCurPage} @@ -237,8 +240,8 @@ const Page = () => { {/* 파일 업로드 모달 */} { /> } /> + {/* 엑셀 데이터 테이블 모달 */} + } + useDirectRender={true} + /> ) } diff --git a/apps/sn/app/(with-base-layout)/oper/floodPre/tableModal.tsx b/apps/sn/app/(with-base-layout)/oper/floodPre/tableModal.tsx new file mode 100644 index 000000000..80cd94a0e --- /dev/null +++ b/apps/sn/app/(with-base-layout)/oper/floodPre/tableModal.tsx @@ -0,0 +1,248 @@ +'use client' +import { useState, useEffect } from 'react' +import LoadingOverlay from '@/components/ui/LoadingBar' +import { UITab, UiTable } from '@common/business_components/ui' +import * as XLSX from 'xlsx'; + +const TABS = [ + { val: "management", text: "관리SHEET" }, + { val: "Inspection results", text: "운영관리현황 점검결과" }, +]; + +interface TableModalProps { + fileNm: string; + atflId: string; +} + +const TableModal: React.FC = ({ fileNm, atflId }) => { + + const [activeTab, setActiveTab] = useState(TABS[0]?.val ?? "management"); + const [managementTableHeader, setManagementTableHeader] = useState<{ id: string; title: string }[]>([]) //테이블 헤더 + const [managementTableData, setManagementTableData] = useState([{}]) //테이블 데이터 + const [resultTableHeader, setResultTableHeader] = useState<{ id: string; title: string }[]>([]) //테이블 헤더 + const [resultTableData, setResultTableData] = useState([{}]) //테이블 데이터 + const [isLoading, setIsLoading] = useState(false) //로딩 상태 + + useEffect(() => { + isChckFile(); + }, []); + + // 파일 다운로드 + const isChckFile = async () => { + setIsLoading(true); + try { + if (fileNm === '' || fileNm === undefined) { + alert('파일을 선택해주세요') + setIsLoading(false); + return false + } + + const response = await fetch(`/attach-service/api/v1/download/${atflId}`); + if (!response.ok) { + throw new Error('파일을 가져오는 중 오류가 발생했습니다.'); + } + + const blob = await response.blob(); + + const file = new File([blob], fileNm, { + type: blob.type || 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' + }); + + const targetSheets = ['관리SHEET', '운영관리현황 점검결과']; + const jsonDataBySheet = await filePickedSelectedSheets(file, 0, targetSheets); + + // 관리 SHEET + const managementHeader = jsonDataBySheet['관리SHEET']?.[7] || []; + // 1번째~47번째, 74번째~80번째 데이터만 필터링 + const filteredManagementHeader = Object.entries(managementHeader) + .filter(([key, value]) => { + const match = key.match(/^__EMPTY_(\d+)$/); + if (match) { + const index = parseInt(match[1] ?? '', 10); + return (index > 0 && index < 47) || (index > 73); + } + return false; + }) + .map(([key, value]) => ({ + id: key, + title: String(value).replace(/\r\n/g, '\n') + })); + + setManagementTableHeader(filteredManagementHeader); + console.log('managementTableHeader', filteredManagementHeader); + + const managementData = Array.isArray(jsonDataBySheet['관리SHEET']) + ? jsonDataBySheet['관리SHEET'].slice(8) + : []; + // console.log('변환 후:', excelToManagementData(managementData)); + const filteredManagementData = filterEmptyRows(managementData); + setManagementTableData(filteredManagementData); + console.log('managementTableData', filteredManagementData); + + // 운영관리현황 점검결과 + const resultHeader = jsonDataBySheet['운영관리현황 점검결과']?.[2] || []; + const dynamicInspectionHeader = Object.entries(resultHeader) + .map(([key, value]) => ({ + id: key, + title: String(value).replace(/\r\n/g, '\n') + })); + dynamicInspectionHeader.splice(1, 0, { + id: "__EMPTY_2", + title: '' + }); + setResultTableHeader(dynamicInspectionHeader); + console.log('resultTableHeader', dynamicInspectionHeader); + + const resultData = Array.isArray(jsonDataBySheet['운영관리현황 점검결과']) + ? jsonDataBySheet['운영관리현황 점검결과'].slice(3) + : []; + const filteredResultData = filterEmptyRows(resultData); + setResultTableData(filteredResultData); + console.log('resultTableData', filteredResultData); + } catch (error) { + console.error('파일 처리 중 오류 발생:', error); + alert('파일 처리 중 오류가 발생했습니다.'); + } finally { + setIsLoading(false); + } + } + + function filterEmptyRows(data: any[]) { + return data + .filter(row => { + return Object.entries(row).some(([key, value]) => { + const match = key.match(/^__EMPTY_(\d+)$/); + if (!match || parseInt(match[1] ?? '', 10) < 2) return false; + + return value !== undefined && + value !== null && + value !== '' && + value !== '-' && + String(value).trim() !== ''; + }); + }) + .map((row) => { + const processedRow = { ...row }; + const keys = Object.keys(processedRow); + + keys.forEach((key, keyIndex) => { + // 마지막 속성(비고)이 아닌 경우에만 빈 값을 '-'으로 변경 + if (keyIndex !== keys.length - 1) { + const value = processedRow[key]; + if (value === undefined || + value === null || + value === '' || + String(value).trim() === '') { + processedRow[key] = '-'; + } + } + }); + + return processedRow; + }); + }; + + // 특정 시트만 선택적으로 JSON으로 변환하는 함수 + function filePickedSelectedSheets(file: File, headerNum: number = 0, sheetNames: string[]): Promise> { + const reader = new FileReader(); + + return new Promise((resolve, reject) => { + reader.onload = function (e) { + const data = e.target?.result; + if (!data) { + console.error("Failed to read file: e.target.result is null"); + reject(new Error("Failed to read file")); + return; + } + + // Excel 파일 읽기 + const wb = XLSX.read(data, { type: 'binary', cellDates: true }); + const result: Record = {}; + + // 지정된 시트 이름만 찾아서 처리 + sheetNames.forEach(targetSheetName => { + // 대소문자 구분 없이 시트 이름 찾기 + const sheetName = wb.SheetNames.find(name => + name.toLowerCase() === targetSheetName.toLowerCase() + ); + + if (sheetName) { + const sheet = wb.Sheets[sheetName]; + if (sheet) { + console.log(`Found sheet: "${sheetName}"`); + const jsonData = XLSX.utils.sheet_to_json(sheet, { range: headerNum, raw: false }); + result[targetSheetName] = jsonData; + } + } else { + console.warn(`Sheet "${targetSheetName}" not found in workbook`); + result[targetSheetName] = []; + } + }); + + resolve(result); + }; + + reader.onerror = function () { + console.error("Error reading file"); + reject(new Error("Error reading file")); + }; + + // 파일 읽기 시작 + reader.readAsBinaryString(file); + }); + }; + + + return ( + <> + {/* 탭 영역 */} + ({ + value: tab.val, + label: tab.text + }))} + onValueChange={(value: string) => setActiveTab(value)} + /> + {/* 관리SHEET 탭 */} + {activeTab === 'management' && ( +
+
+ +
+ { }} + /> +
+
+
+ )} + {/* 운영관리현황 점검결과 탭 */} + {activeTab === 'Inspection results' && ( +
+
+ + { }} + /> +
+
+ )} + + ) +}; + +export default TableModal; \ No newline at end of file diff --git a/apps/sn/app/(with-base-layout)/oper/oldPp/page.tsx b/apps/sn/app/(with-base-layout)/oper/oldPp/page.tsx index efcea5461..7165188ca 100644 --- a/apps/sn/app/(with-base-layout)/oper/oldPp/page.tsx +++ b/apps/sn/app/(with-base-layout)/oper/oldPp/page.tsx @@ -205,7 +205,7 @@ const Page = () => {

사업정보 등록현황

- +
diff --git a/apps/sn/app/components/lib/api/index.ts b/apps/sn/app/components/lib/api/index.ts index df1d9f509..f70b0f3c0 100644 --- a/apps/sn/app/components/lib/api/index.ts +++ b/apps/sn/app/components/lib/api/index.ts @@ -1,8 +1,9 @@ import { useEffect, useState } from "react"; +import axios from 'axios'; const BASE_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL || ''; -export const getData = async (url: string, data?: object): Promise => { +export const getData = async (url: string): Promise => { const res = await fetch(url.startsWith('/') ? `${url}` : `${BASE_URL}/${url}`, { method: "GET", }); @@ -48,13 +49,8 @@ export const postData = async (url: string, data: object): Promise => { }; export const postMutiPart = async (url: string, data: FormData): Promise => { - const options: RequestInit = { - method: "POST", - body: data, - }; - const res = await fetch(`${BASE_URL}/${url}`, options); - const resData = await res.json(); - return resData; + const res = await axios.post(`${BASE_URL}/${url}`, data); + return res.data; }; export const putData = async (url: string, data: object): Promise => { diff --git a/apps/sn/app/components/ui/fileUploadForm.tsx b/apps/sn/app/components/ui/fileUploadForm.tsx index 0aed37bc9..1272488a1 100644 --- a/apps/sn/app/components/ui/fileUploadForm.tsx +++ b/apps/sn/app/components/ui/fileUploadForm.tsx @@ -41,23 +41,84 @@ const FileUploadForm = ({ const [selectedYear, setSelectedYear] = useState(yearOptions[0]?.val || ''); const [selectedQuarter, setSelectedQuarter] = useState(quater[0]?.val || ''); - const internalHandleUploadResponse = (file: File) => async (res: HttpResponse) => { - const fileInfo = res.getHeader("X-File-Info"); - if (!fileInfo) { - if (onFileSelect) onFileSelect(file, null, null); + function registFile() { + console.log(files?.[0]?.name) + if (!currentFile) { + alert('파일을 선택해주세요.'); return; } - const { atflCd: newAtflCd, atflId: newAtflId } = JSON.parse(fileInfo) ?? {}; - const orgnlFileNm = file.name; - console.log(`FileUploadForm (idx: ${fileIdx ?? 'N/A'}) - orgnlFileNm:`, orgnlFileNm, 'New AtflCd:', newAtflCd, 'New AtflId:', newAtflId); - setFileName(orgnlFileNm); - setInternalAtflCd(newAtflCd); - setInternalAtflId(newAtflId); - - if (onFileSelect) { - onFileSelect(file, newAtflCd, newAtflId); + if (!selectedYear) { + alert('등록년도를 선택해주세요.'); + return; } - }; + if (!selectedQuarter) { + alert('분기를 선택해주세요.'); + return; + } + + const fileNm = currentFile.name; + if (onSubmit && files && currentFile) { + // Promise로 handleUpload 완료를 기다림 + handleUploadWithResponse(files, currentFile) + .then(({ atflCd, atflId }) => { + // 업로드 완료 후 onSubmit 실행 + onSubmit(fileNm, selectedYear, selectedQuarter, atflCd || '', atflId || ''); + + // UI 초기화 + setFiles(null); + setFileName(''); + setUploaderKey(prev => prev + 1); + }) + .catch((error) => { + console.error('파일 업로드 실패:', error); + alert('파일 업로드 중 오류가 발생했습니다.'); + }); + } + } + + // handleUpload를 Promise로 감싸는 헬퍼 함수 + function handleUploadWithResponse(files: FileList, file: File): Promise<{ atflCd: string | null, atflId: string | null }> { + return new Promise((resolve, reject) => { + const uploadResponseHandler = async (res: HttpResponse) => { + try { + const fileInfo = res.getHeader("X-File-Info"); + if (!fileInfo) { + resolve({ atflCd: null, atflId: null }); + return; + } + + const { atflCd: newAtflCd, atflId: newAtflId } = JSON.parse(fileInfo) ?? {}; + console.log(`FileUploadForm (idx: ${fileIdx ?? 'N/A'}) - Upload completed:`, newAtflCd, newAtflId); + + // 내부 상태도 업데이트 (다른 용도로 사용할 수 있음) + setInternalAtflCd(newAtflCd); + setInternalAtflId(newAtflId); + setFileName(file.name); + + if (onFileSelect) { + onFileSelect(file, newAtflCd, newAtflId); + } + + // Promise 완료 + resolve({ atflCd: newAtflCd, atflId: newAtflId }); + } catch (error) { + reject(error); + } + }; + + const uploadErrorHandler = (error: Error) => { + console.error('Upload failed:', error); + reject(error); + }; + + // 기존 handleUpload 유틸리티 사용 (수정하지 않음) + handleUpload(files, { + handleUploadStart: setUpload, + handleSuccess: setUpload, + handleResponse: uploadResponseHandler, + }); + }); + } return ( @@ -87,19 +148,12 @@ const FileUploadForm = ({ if (!firstFile) return; // 타입스크립트 undefined 가능성 제거 setFiles(selectedFiles); + setFileName(firstFile.name); setCurrentFile(firstFile); if (onFileSelect) { onFileSelect(firstFile, internalAtflCd, internalAtflId); } - - handleUpload(selectedFiles, { - handleUploadStart: setUpload, - handleSuccess: setUpload, - // handleResponse는 tus-js-client의 Upload 인스턴스 콜백과 연동 필요 - // 여기서는 handleUpload 유틸리티가 응답을 직접 처리한다고 가정 - handleResponse: internalHandleUploadResponse(firstFile), - }); }} customPreview={ showPreview ? ( @@ -116,30 +170,7 @@ const FileUploadForm = ({ diff --git a/apps/sn/app/components/ui/formSection.tsx b/apps/sn/app/components/ui/formSection.tsx index 384808aee..4f33fbbe5 100644 --- a/apps/sn/app/components/ui/formSection.tsx +++ b/apps/sn/app/components/ui/formSection.tsx @@ -6,12 +6,13 @@ interface FormSectionProps { titleNm: string form: any name: string + required?: boolean } -const FormSection = ({ children, titleNm, form, name }: FormSectionProps) => { +const FormSection = ({ children, titleNm, form, name, required }: FormSectionProps) => { return (
-
{titleNm}
+
{titleNm}{required && *}
void; onApply: () => void; }) { - const { map, code, codeInit, findRuseFclt } = useWrStore( + const { map, code, findRuseFclt } = useWrStore( (state) => state ); const [sidoValue, setSidoValue] = useState(""); @@ -24,10 +24,7 @@ export function SearchBar({ { text: "전체", val: "0" }, ]); - useEffect(() => { - //codeInit(); - }, [codeInit]); - + const initializeSigunData = useCallback( (sidoCd: string) => { if (sidoCd === "0") { diff --git a/apps/wr/app/(with-layout)/matMng/coMat/coMatRvw/page.tsx b/apps/wr/app/(with-layout)/matMng/coMat/coMatRvw/page.tsx index b85a04d82..a3ed891a8 100644 --- a/apps/wr/app/(with-layout)/matMng/coMat/coMatRvw/page.tsx +++ b/apps/wr/app/(with-layout)/matMng/coMat/coMatRvw/page.tsx @@ -585,7 +585,7 @@ export default function CoMatRvw() { control={form.control} name="ph" render={({ field }) => ( - +
0) { const rainData = tableData(rainRes, { code }).map( (item: customRainBdst) => { - const archArea = - item?.archArea.split(".")[0] !== "-1" - ? item?.archArea.split(".")[0] - : "확인불가"; return { ...item, - archArea: formatCommaNumber(archArea), + archArea: formatArchArea(item?.archArea,1), bdstChgYn: item.chgBuilding[0]?.bdstChgNm != null ? "Y" : "N", + frstArchPrmsnDt:item?.frstArchPrmsnDt||'확인불가' }; } ); @@ -129,7 +126,7 @@ export default function Home() { { @@ -140,9 +137,9 @@ export default function Home() { {/* */}
-
- - +
+ +
- + (""); useEffect(() => { - htdflSttnSearch(false, pageNation, "20", String(getConValue()), fcltNm, code.cityCd, code.sggCd); + htdflSttnSearch(false, pageNation, htdflSttnSize, String(getConValue()), fcltNm, code.cityCd, code.sggCd); isResetPage(false) }, [pageNation]); @@ -102,7 +103,7 @@ export default function HtdflSttn() { const excelClick = useCallback(() => { isDownloadTriggered.current = true; - htdflSttnSearch(true, 1, String(htdflSttnResCount), String(getConValue()), fcltNm, code.cityCd, code.sggCd); + htdflSttnSearch(true, 1, htdflSttnResCount, String(getConValue()), fcltNm, code.cityCd, code.sggCd); }, [htdflSttnSearch, objSearch, code.cityCd, code.sggCd, fcltNm, htdflSttnResCount]); useEffect(() => { @@ -124,7 +125,7 @@ export default function HtdflSttn() { useEffect(() => { if (!htdflSttn.isInit) { - initHtdfl(false, 1, "20", "0", fcltNm, code.cityCd, code.sggCd); + initHtdfl(false, 1, htdflSttnSize, "0", fcltNm, code.cityCd, code.sggCd); } }, [htdflSttn.isInit, initHtdfl]); @@ -185,7 +186,7 @@ export default function HtdflSttn() {
diff --git a/apps/wr/components/ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx b/apps/wr/components/ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx index 0d1b47f4a..5ae5cd264 100644 --- a/apps/wr/components/ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx +++ b/apps/wr/components/ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx @@ -19,9 +19,9 @@ import { FormUi, SelectBox, } from "@common/business_components/ui"; -import { Button, Input, ScrollArea } from "@common/components/ui"; +import { Button, Input } from "@common/components/ui"; import { zodResolver } from "@hookform/resolvers/zod"; -import { useEffect, useState } from "react"; +import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { z } from "zod"; import BdstAdd from "../bdstReg/bdstAdd"; @@ -46,18 +46,11 @@ export default function RswtrRegForm({ const { code, rswtBdstUpdate, rswtBdstReg } = useWrStore( (state) => state ); - const [divStyle, setDivStyle] = useState("h-[53vh]"); + // const [divStyle, setDivStyle] = useState("h-[53vh]"); useEffect(() => { if(isMap){ - setDivStyle("h-[100vh]") - }else if (isModal === true) { - setDivStyle("h-[53vh] max-h-[37vh]") - } else if (readonly === true) { - setDivStyle("h-[62vh]") - } else { - setDivStyle("h-[57vh]") } - }, [isModal, readonly ,isMap]); + }, [isMap]); // const defaultValues: z.infer = rswtZod.parse({}); const form = useForm>({ @@ -176,7 +169,7 @@ export default function RswtrRegForm({ )}
- + {/* */} {isModal && rswtForm != undefined ? : <>} {isModal ? (

@@ -506,7 +499,7 @@ export default function RswtrRegForm({

- + {/* */}
diff --git a/apps/wr/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal.tsx b/apps/wr/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal.tsx index d1de75ba1..31f9f2d16 100644 --- a/apps/wr/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal.tsx +++ b/apps/wr/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal.tsx @@ -196,7 +196,7 @@ export function BdstReg({
- {/* */} +
{moType === "RNB" ? ( ({ > )}
- {/*
*/} +
); diff --git a/apps/wr/components/ui/modal/sttn/sttnDetailModal/coDatailModal.tsx b/apps/wr/components/ui/modal/sttn/sttnDetailModal/coDatailModal.tsx index 80cdb5c4e..b23cad340 100644 --- a/apps/wr/components/ui/modal/sttn/sttnDetailModal/coDatailModal.tsx +++ b/apps/wr/components/ui/modal/sttn/sttnDetailModal/coDatailModal.tsx @@ -571,7 +571,7 @@ export default function CoDetailModal({ control={form.control} name="usgRgnArea" render={({ field }) => ( - + { + return String(value).includes('.')? + String(parseFloat(value.toString().replace(/,/g, ''))).split('.').map((part, index) => + index === 0 ? part.replace(/\B(?=(\d{3})+(?!\d))/g, ",") : part + ).join('.') + : String(value).replace(/\B(?=(\d{3})+(?!\d))/g, ","); +} + type BarChartProps = { chartConfig: ChartConfig | undefined; chartData: any[] | undefined; @@ -51,7 +59,7 @@ const CustomTooltip = ({
{/* 색상 박스 추가 (범례 색상과 동일) */}
- {` ${item.name} : ${item.value}`} + {` ${item.name} : ${fncNumberFormet(item.value)}`}
))}
diff --git a/packages/common/business_components/src/ui/chart/LineBarChart.tsx b/packages/common/business_components/src/ui/chart/LineBarChart.tsx index d81d6e2c8..9f9a4feec 100644 --- a/packages/common/business_components/src/ui/chart/LineBarChart.tsx +++ b/packages/common/business_components/src/ui/chart/LineBarChart.tsx @@ -6,6 +6,7 @@ import { type ChartConfig } from "./lib/chart"; import type { ContentType } from "recharts/types/component/DefaultLegendContent"; +import type { set } from "date-fns"; // 툴크 커스텀 css /** 차트 기본 스타일 */ @@ -29,6 +30,7 @@ type LineBarChartProps = { yFieldText?: string | undefined; yFieldText2?: string | undefined; showLegend?: boolean; + setXAxisYearTick?: boolean; } type LegendEntry = { @@ -86,7 +88,8 @@ export default function LineBarchart({ graphSize = 40, yFieldText, yFieldText2, - showLegend = true + showLegend = true, + setXAxisYearTick = false }: LineBarChartProps) { if (!chartData || chartData.length === 0) { return
데이터가 없습니다.
; @@ -105,6 +108,24 @@ export default function LineBarchart({ const lineMaxValue = getMaxValue(chartData, 'lineVal'); const barMaxValue = getMaxValue(chartData, 'barVal'); + const getTicks = (data : any []) => { + const monthSet = new Set(); + const rtnData : string[] = []; + for(const item of data){ + const month = String(item.label).slice(0,7); + if(!monthSet.has(month)){ + monthSet.add(month); + rtnData.push(item.label); + } + } + return rtnData; + } + + let monthTicks : string[] = []; + if(setXAxisYearTick){ + monthTicks = getTicks(chartData); + } + return (
{chartTitle && chartTitle !== "" && chartTitle !== "undefined" ? ( @@ -121,7 +142,10 @@ export default function LineBarchart({ margin={{ top: 5, right: 30, left: 20, bottom: 5 }} > - + {setXAxisYearTick? + value.slice(0,7)} fontSize="8px" fontWeight="bold"/> : + + } {/* 왼쪽 Y축: lineVal */} {yFieldText && 데이터가 없습니다.
; @@ -164,18 +166,24 @@ export default function Linechart({ return acc; }, []); - // 데이터의 최대값 계산 - const getMaxValue = (data: ChartDataType): number => { - return Math.max(...Object.values(data).flatMap(dataset => - dataset.map(entry => { - const val = parseFloat(entry.val); - return isNaN(val) ? 0 : val; - }) - )); - }; - - const lineMaxValue = getMaxValue(chartData); - + const getTicks = (data : any []) => { + const monthSet = new Set(); + const rtnData : string[] = []; + for(const item of data){ + const month = String(item.label).slice(0,7); + if(!monthSet.has(month)){ + monthSet.add(month); + rtnData.push(item.label); + } + } + return rtnData; + } + + let monthTicks : string[] = []; + if(setXAxisYearTick){ + monthTicks = getTicks(mergedData); + } + return (
{chartTitle && chartTitle !== "" && chartTitle !== "undefined" ? ( @@ -192,8 +200,27 @@ export default function Linechart({ margin={{ top: 5, right: 30, left: 20, bottom: 5 }} > - - value.toLocaleString()} tickCount={6} orientation="left" label={{ value: yFieldText, className:"text-[12px] font-bold", angle: -90, position: 'insideLeft' }} /> + + {setXAxisYearTick ? + value.slice(0,7)} + /> : + + } + + } /> {showLegend && } {Object.keys(chartData).map((key) => { diff --git a/packages/common/business_components/src/ui/chart/index.tsx b/packages/common/business_components/src/ui/chart/index.tsx index ee308e378..3a87e3978 100644 --- a/packages/common/business_components/src/ui/chart/index.tsx +++ b/packages/common/business_components/src/ui/chart/index.tsx @@ -56,6 +56,7 @@ type MainChartProps = { yFieldText? : string | undefined; yFieldText2? : string | undefined; showLegend? : boolean; + setXAxisYearTick ?: boolean; } function MainChart(props: MainChartProps) { @@ -65,7 +66,7 @@ function MainChart(props: MainChartProps) { ); } else if( props.chartForm == CHART_FORM.LINE ){ return ( - + ); } else if( props.chartForm == CHART_FORM.DN ){ return ( @@ -73,7 +74,7 @@ function MainChart(props: MainChartProps) { ); } else if (props.chartForm == CHART_FORM.LINEBAR){ return ( - + ); } else if (props.chartForm == CHART_FORM.LINEBAR2){ return ( diff --git a/packages/common/business_components/src/ui/index.ts b/packages/common/business_components/src/ui/index.ts index eab3a7eab..8604f80d5 100644 --- a/packages/common/business_components/src/ui/index.ts +++ b/packages/common/business_components/src/ui/index.ts @@ -14,6 +14,7 @@ export * from "./search_form_left"; export * from "./search_form_right"; export * from "./search_input"; export * from "./search_input_select"; +export * from "./search_input_address"; export * from "./select"; export * from "./tab"; export * from "./table"; 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 f8fdb2a24..9a7ffe55b 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 @@ -688,6 +688,111 @@ GisOlLayer.prototype = { }); return source; + }, + /** + * 여러 개의 검색 결과를 GeoJSON으로 표시 + * @param {string} lyrTyNm 레이어 타입명 + * @param {Array|string} featureIds 피처 ID 배열 또는 단일 ID + */ + showMultipleSearchResultsFromGeoJSON: function (lyrTyNm, featureIds) { + const layer = this.ensureSearchLayer(); + + const prev = layer.getSource(); + if (prev) prev.clear(true); + + const layerCode = GisApp.layerCode; + const matched = Object.values(layerCode).find(obj => obj.lyrTyNm === lyrTyNm); + layer.set("gobjTy", matched.gobjTy); + + const source = this.getMultiFeatureSourceFromGeoserver(lyrTyNm, featureIds); + + const onLoadEnd = async () => { + const feats = source.getFeatures(); + if (feats && feats.length > 0) { + const select = GisApp.Module.select; + await select.resetSelectedFeature(); + for (const feature of feats) select.selectMultiFeature(feature, true); + const geometries = feats.map(f => f.getGeometry()); + if (geometries.length > 0) { + const extent = this.combineExtents(geometries.map(g => g?.getExtent())); + + let map = GisApp.Module.core.map; + map.getView().fit(extent, { + duration: 300, + padding: [50, 50, 50, 50], + maxZoom: 16 + }); + } + } else { + console.warn('[search-layer] GeoJSON에 피처가 없습니다.'); + } + source.un('featuresloadend', onLoadEnd); + source.un('featuresloaderror', onLoadError); + }; + + const onLoadError = (e) => { + console.error('[search-layer] GeoJSON 로드 실패:', e); + source.un('featuresloadend', onLoadEnd); + source.un('featuresloaderror', onLoadError); + }; + + source.once('featuresloadend', onLoadEnd); + source.once('featuresloaderror', onLoadError); + + layer.setSource(source); + + source.refresh(); + }, + /** + * 피쳐 ID로 지오서버에서 검색해서 여러개 피처의 벡터소스를 리턴 + * @param {} lyrTyNm + * @param {*} featureId + * @returns + */ + getMultiFeatureSourceFromGeoserver: function (lyrTyNm, featureId) { + + const typeName = this.storageNm + lyrTyNm; + const srsName = GisApp.TO_PROJ; + const baseUrl = GisApp.GEOSERVER_URL; + + const ids = Array.isArray(featureId) ? featureId : [featureId]; + + const source = new VectorSource({ + format: new GeoJSON(), + url: () => { + const params = new URLSearchParams({ + SERVICE: 'WFS', + VERSION: '1.1.0', + REQUEST: 'GetFeature', + TYPENAME: typeName, + SRSNAME: srsName, + OUTPUTFORMAT: 'application/json', + FEATUREID: ids.join(','), + }); + return `${baseUrl}?${params.toString()}`; + }, + strategy: all, + }); + + return source; + }, + /** + * 여러 extent 배열을 받아 결합된 extent를 반환 + * @param {Array>} extents [[minX,minY,maxX,maxY], ...] + * @returns {Array|null} + */ + combineExtents: function (extents) { + if (!extents || extents.length === 0) return null; + let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity; + for (const e of extents) { + if (!e || e.length < 4) continue; + minX = Math.min(minX, e[0]); + minY = Math.min(minY, e[1]); + maxX = Math.max(maxX, e[2]); + maxY = Math.max(maxY, e[3]); + } + if (!isFinite(minX) || !isFinite(minY) || !isFinite(maxX) || !isFinite(maxY)) return null; + return [minX, minY, maxX, maxY]; } }; diff --git a/packages/common/business_components/src/ui/sdmInsertForm/form.tsx b/packages/common/business_components/src/ui/sdmInsertForm/form.tsx index 20a2050bc..5099db92e 100644 --- a/packages/common/business_components/src/ui/sdmInsertForm/form.tsx +++ b/packages/common/business_components/src/ui/sdmInsertForm/form.tsx @@ -1,47 +1,48 @@ import { Input } from "@common/components"; import { AsYouType } from "libphonenumber-js"; -import React, { useCallback, useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useState } from "react"; import type { ControllerRenderProps, UseFormReturn } from "react-hook-form"; import { FormField } from "../form"; import { FormUi } from "../form_ui"; import { SelectBox } from "../select"; import { SdmSearchForm } from "./sdmNameBox"; +import SearchInputAddress from "../search_input_address"; +import type { addressType } from "../search_input_address/findAddress"; // 수요처 정보 export type DemandCompType = { - index : number | ''; - dmdInfoId : 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 | ''; - tn : number | ''; - combResdch : number | ''; - tph : number | ''; - trbt : number | ''; - ph : number | ''; - blgcOxygDmndQnt : number | ''; - clIonQnt : number | ''; - smll : number | ''; - cdtnv : number | ''; - crmty : number | ''; - rmrk : string | ''; - reqDt : string | ''; -} - + index: number | ""; + dmdInfoId: 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 | ""; + tn: number | ""; + combResdch: number | ""; + tph: number | ""; + trbt: number | ""; + ph: number | ""; + blgcOxygDmndQnt: number | ""; + clIonQnt: number | ""; + smll: number | ""; + cdtnv: number | ""; + crmty: number | ""; + rmrk: string | ""; + reqDt: string | ""; +}; type SelectDataType = { - text: string; - val: string; + text: string; + val: string; }; interface FormSchema { @@ -52,7 +53,7 @@ interface FormSchema { daddr: string; telno: string; bznmTotNo?: string; - rgtr?: string | ''; + rgtr?: string | ""; dmdAplyPswd?: string; dmdAplyPswdConfirm?: string; } @@ -65,38 +66,50 @@ interface SdmRegFormProps { dataList: DemandCompType[]; rtnValue: (searchText: string, pageCnt: number) => void; setIsDuplicate: (value: boolean) => void; - updateList?:DemandCompType - readOnly?:boolean + updateList?: DemandCompType; + readOnly?: boolean; } -type FieldComponent = (field: ControllerRenderProps) => JSX.Element; +type FieldComponent = ( + field: ControllerRenderProps, +) => JSX.Element; -export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigunData, dataList, rtnValue, setIsDuplicate ,updateList,readOnly}: SdmRegFormProps) { - const [sidoValue, setSidoValue] = useState(''); - const [sigunValue, setSigunValue] = useState(''); - const [filteredSigunData, setFilteredSigunData] = useState([{ text: "전체", val: '0' }]); +export function SdmRegForm({ + form, + onCheckboxChange, + selectSidoData, + selectSigunData, + dataList, + rtnValue, + setIsDuplicate, + updateList, + readOnly, +}: SdmRegFormProps) { + const [sidoValue, setSidoValue] = useState(""); + const [sigunValue, setSigunValue] = useState(""); + const [filteredSigunData, setFilteredSigunData] = useState([ + { text: "전체", val: "0" }, + ]); const [isBznmTotNoDisabled, setIsBznmTotNoDisabled] = useState(true); - const [orgBznmNo, setOrgBznmNo] = useState(''); - const [updateCoNm, setUpdateNm] = useState('') + const [orgBznmNo, setOrgBznmNo] = useState(""); + const [updateCoNm, setUpdateNm] = useState(""); - - - const formatPhoneNumber = (value:string) => { + const formatPhoneNumber = (value: string) => { const input = value; const formatter = new AsYouType("KR"); const formatted = formatter.input(input); - return formatted + return formatted; }; // 사업자등록번호 포맷팅 함수 (3-2-5 형식) const formatBusinessNumber = (value: string) => { // 숫자만 추출 const digitsOnly = value.replace(/\D/g, ""); - + // 10자리까지만 처리 const limitedDigits = digitsOnly.slice(0, 10); - + // 3-2-5 형식으로 포맷팅 if (limitedDigits.length <= 3) { return limitedDigits; @@ -107,136 +120,166 @@ export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigun } }; - - const initializeSigunData = useCallback((sidoCd: string) => { - if (sidoCd === '0') { - setSigunValue('0'); - setFilteredSigunData([{ text: "전체", val: '0' }]); - } else { - const newFilteredData = [ - { text: "전체", val: '0' }, - ...selectSigunData.filter((item: SelectDataType) => item.val.substring(0, 2) === sidoCd) - ]; - setSigunValue('0'); - setFilteredSigunData(newFilteredData); - } - }, [selectSigunData]); - - const onSelectValue = useCallback(( - val: string, - type: string, - field: ControllerRenderProps | undefined - ) => { - if(!updateList?.dmdInfoId) - switch (type) { - case "ctpvCd": - initializeSigunData(val); - setSidoValue(val); - field?.onChange(val); - form.setValue('dmdInfoId', ''); - form.setValue('bznmTotNo', ''); - break; - case "sggCd": - setSigunValue(val); - field?.onChange(val); - form.setValue('dmdInfoId', ''); - form.setValue('bznmTotNo', ''); - break; - } - }, [initializeSigunData, form,updateList]); - - const handleCheckboxChange = useCallback((checked: boolean) => { - setIsBznmTotNoDisabled(checked); - onCheckboxChange?.(checked); - }, [onCheckboxChange]); - - const setRtnValue = useCallback((searchText: string, pageCnt: number) => { - rtnValue(searchText, pageCnt); - }, [rtnValue]); - - const setRtnData = useCallback((item: DemandCompType) => { - const rtnCompData = item; - - form.setValue('dmdCoNm', rtnCompData?.dmdCoNm || ''); - form.setValue('dmdInfoId', rtnCompData?.dmdInfoId.substring(0,10) || ''); - form.setValue('bznmTotNo', rtnCompData?.dmdInfoId.substring(10,14)==='0000' ? '' : rtnCompData?.dmdInfoId.substring(10,14)); - - // 수요처 불러온 후 사업자번호를 바꿀경우를 대비함 - setOrgBznmNo(rtnCompData?.dmdInfoId); - const sidoCd = rtnCompData?.ctpvCd.trim() || ''; - setSidoValue(sidoCd); - form.setValue('ctpvCd', sidoCd); - initializeSigunData(sidoCd); - - setTimeout(() => { - const sigunCd = rtnCompData?.sggCd.trim() || ''; - setSigunValue(sigunCd); - form.setValue('sggCd', sigunCd); - }, 100); - - if(rtnCompData?.dmdInfoId.substring(10,14) === '0000') { - handleCheckboxChange(true); - } else { - handleCheckboxChange(false); - } - - form.setValue('daddr', rtnCompData?.daddr || ''); - form.setValue('telno', rtnCompData?.telno || ''); - - // 선택을 했을 때 중복 상태를 true로 설정 - setIsDuplicate(true); - }, [form, handleCheckboxChange, initializeSigunData, setIsDuplicate]); - - const setRtnText = useCallback((text: string) => { - form.setValue('dmdCoNm', text); - }, [form]); - - const bznNoChange = useCallback((e: React.ChangeEvent, noType: string, field?: ControllerRenderProps) => { - const value = e.target.value; - - if(noType === 'dmdInfoId') { - // 숫자만 추출하여 저장 (포맷팅 제거) - const digitsOnly = value.replace(/\D/g, ""); - - if(orgBznmNo.substring(0,10) === digitsOnly) { - setIsDuplicate(true); + const initializeSigunData = useCallback( + (sidoCd: string) => { + if (sidoCd === "0") { + setSigunValue("0"); + setFilteredSigunData([{ text: "전체", val: "0" }]); } else { - setIsDuplicate(false); + const newFilteredData = [ + { text: "전체", val: "0" }, + ...selectSigunData.filter( + (item: SelectDataType) => item.val.substring(0, 2) === sidoCd, + ), + ]; + setSigunValue("0"); + setFilteredSigunData(newFilteredData); } - field?.onChange(digitsOnly); - } else { - if(orgBznmNo.substring(10,14) === value) { - setIsDuplicate(true); - } else { - setIsDuplicate(false); - } - field?.onChange(value); - } - }, [orgBznmNo, setIsDuplicate]); + }, + [selectSigunData], + ); - - useEffect(() => { - form.setValue('rgtr', '2d4b151d-1828-41e0-91d9-4cd28ea1cc7e'); - - if(updateList?.dmdInfoId){ - form.reset(updateList) - if(updateList?.dmdInfoId.slice(10,14) !== '0000') { - handleCheckboxChange( false); - form.setValue('bznmTotNo', updateList?.dmdInfoId.slice(10,14)); - } else { - handleCheckboxChange( true); + const onSelectValue = useCallback( + ( + val: string, + type: string, + field: ControllerRenderProps | undefined, + ) => { + if (!updateList?.dmdInfoId) + switch (type) { + case "ctpvCd": + initializeSigunData(val); + setSidoValue(val); + field?.onChange(val); + form.setValue("dmdInfoId", ""); + form.setValue("bznmTotNo", ""); + break; + case "sggCd": + setSigunValue(val); + field?.onChange(val); + form.setValue("dmdInfoId", ""); + form.setValue("bznmTotNo", ""); + break; } - form.setValue('dmdInfoId', updateList?.dmdInfoId.slice(0,10)); - setUpdateNm(updateList?.dmdCoNm) - // setSigunValue(updateList?.sggCd) - }else{ - handleCheckboxChange( true); - } - }, [updateList,form]); + }, + [initializeSigunData, form, updateList], + ); + + const handleCheckboxChange = useCallback( + (checked: boolean) => { + setIsBznmTotNoDisabled(checked); + onCheckboxChange?.(checked); + }, + [onCheckboxChange], + ); + + const setRtnValue = useCallback( + (searchText: string, pageCnt: number) => { + rtnValue(searchText, pageCnt); + }, + [rtnValue], + ); + + const setRtnData = useCallback( + (item: DemandCompType) => { + const rtnCompData = item; + + form.setValue("dmdCoNm", rtnCompData?.dmdCoNm || ""); + form.setValue("dmdInfoId", rtnCompData?.dmdInfoId.substring(0, 10) || ""); + form.setValue( + "bznmTotNo", + rtnCompData?.dmdInfoId.substring(10, 14) === "0000" + ? "" + : rtnCompData?.dmdInfoId.substring(10, 14), + ); + + // 수요처 불러온 후 사업자번호를 바꿀경우를 대비함 + setOrgBznmNo(rtnCompData?.dmdInfoId); + const sidoCd = rtnCompData?.ctpvCd.trim() || ""; + setSidoValue(sidoCd); + form.setValue("ctpvCd", sidoCd); + initializeSigunData(sidoCd); + + setTimeout(() => { + const sigunCd = rtnCompData?.sggCd.trim() || ""; + setSigunValue(sigunCd); + form.setValue("sggCd", sigunCd); + }, 100); + + if (rtnCompData?.dmdInfoId.substring(10, 14) === "0000") { + handleCheckboxChange(true); + } else { + handleCheckboxChange(false); + } + + form.setValue("daddr", rtnCompData?.daddr || ""); + form.setValue("telno", rtnCompData?.telno || ""); + + // 선택을 했을 때 중복 상태를 true로 설정 + setIsDuplicate(true); + }, + [form, handleCheckboxChange, initializeSigunData, setIsDuplicate], + ); + + const setRtnText = useCallback( + (text: string) => { + form.setValue("dmdCoNm", text); + }, + [form], + ); + + const bznNoChange = useCallback( + ( + e: React.ChangeEvent, + noType: string, + field?: ControllerRenderProps, + ) => { + const value = e.target.value; + + if (noType === "dmdInfoId") { + // 숫자만 추출하여 저장 (포맷팅 제거) + const digitsOnly = value.replace(/\D/g, ""); + + if (orgBznmNo.substring(0, 10) === digitsOnly) { + setIsDuplicate(true); + } else { + setIsDuplicate(false); + } + field?.onChange(digitsOnly); + } else { + if (orgBznmNo.substring(10, 14) === value) { + setIsDuplicate(true); + } else { + setIsDuplicate(false); + } + field?.onChange(value); + } + }, + [orgBznmNo, setIsDuplicate], + ); useEffect(() => { - console.log( 'form.formState.errors ', form.formState.errors) - },[form.formState.errors]) + form.setValue("rgtr", "2d4b151d-1828-41e0-91d9-4cd28ea1cc7e"); + + if (updateList?.dmdInfoId) { + form.reset(updateList); + if (updateList?.dmdInfoId.slice(10, 14) !== "0000") { + handleCheckboxChange(false); + form.setValue("bznmTotNo", updateList?.dmdInfoId.slice(10, 14)); + } else { + handleCheckboxChange(true); + } + form.setValue("dmdInfoId", updateList?.dmdInfoId.slice(0, 10)); + setUpdateNm(updateList?.dmdCoNm); + // setSigunValue(updateList?.sggCd) + } else { + handleCheckboxChange(true); + } + }, [updateList, form]); + + useEffect(() => { + console.log("form.formState.errors ", form.formState.errors); + }, [form.formState.errors]); const formFields: Array<{ name: keyof FormSchema; @@ -246,53 +289,64 @@ export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigun { name: "ctpvCd", label: "시도", - component: (field) => ( + component: (field) => updateList?.dmdInfoId ? ( {field.onChange(e.target.value)} } - value={ selectSidoData.find(item => item.val === field.value?.trim() )?.text??''} - readOnly={!!updateList?.dmdInfoId} + onChange={(e) => { + field.onChange(e.target.value); + }} + value={ + selectSidoData.find((item) => item.val === field.value?.trim()) + ?.text ?? "" + } + readOnly={!!updateList?.dmdInfoId} /> ) : ( - onSelectValue(val.toString(), 'ctpvCd', field??'')} - className="" - value={sidoValue} - /> - ) - ) + + onSelectValue(val.toString(), "ctpvCd", field ?? "") + } + className="" + value={sidoValue} + /> + ), }, { name: "sggCd", label: "시군구", - component: (field) => ( + component: (field) => updateList?.dmdInfoId ? ( {field.onChange(e.target.value)} } - value={selectSigunData.find(item => item.val === field.value?.trim() )?.text??''} - readOnly={!!updateList?.dmdInfoId} + onChange={(e) => { + field.onChange(e.target.value); + }} + value={ + selectSigunData.find((item) => item.val === field.value?.trim()) + ?.text ?? "" + } + readOnly={!!updateList?.dmdInfoId} /> ) : ( - onSelectValue(val.toString(), 'sggCd', field)} - className="" - value={sigunValue} - /> - - ) - ) + + onSelectValue(val.toString(), "sggCd", field) + } + className="" + value={sigunValue} + /> + ), }, { name: "dmdCoNm", @@ -308,117 +362,138 @@ export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigun readOnly={readOnly} {...field} /> - ) + ), }, { name: "dmdInfoId", label: "사업자등록번호", component: (field) => ( - bznNoChange(e, 'dmdInfoId', field)} - value={formatBusinessNumber(field.value || '')} + onChange={(e) => bznNoChange(e, "dmdInfoId", field)} + value={formatBusinessNumber(field.value || "")} readOnly={!!updateList?.dmdInfoId} /> - ) + ), }, { name: "bznmTotNo", label: "", component: (field) => ( - - bznNoChange(e, 'bznmTotNo', field)} - // {...field} - value={ field.value} + onChange={(e) => bznNoChange(e, "bznmTotNo", field)} + // {...field} + value={field.value} readOnly={!!updateList?.dmdInfoId} /> - ) + ), }, { name: "daddr", label: "사업자주소", component: (field) => ( - + { + field.onChange(rtnData.roadAddrPart1); + console.log("rtnData ", rtnData); + }} /> - ) + ), }, { name: "telno", label: "전화번호", - component: (field) => - - { - const formattedValue = formatPhoneNumber(field.value??''); // 초기 렌더링 시에도 포맷 적용 + component: (field) => { + const formattedValue = formatPhoneNumber(field.value ?? ""); // 초기 렌더링 시에도 포맷 적용 return ( - { - const value = e.target.value.replace(/\D/g, ''); // 숫자만 저장 - field.onChange(value); - }} - /> - )} + { + const value = e.target.value.replace(/\D/g, ""); // 숫자만 저장 + field.onChange(value); + }} + /> + ); + }, }, - + { name: "dmdAplyPswd", label: "패스워드", component: (field) => ( - - ) + ), }, { name: "dmdAplyPswdConfirm", label: "패스워드확인", component: (field) => ( - - ) - } - ] + ), + }, + ]; return ( <> {formFields - .filter(({ name }) => !(updateList?.dmdInfoId && (name === "dmdAplyPswd" || name === "dmdAplyPswdConfirm"))) + .filter( + ({ name }) => + !( + updateList?.dmdInfoId && + (name === "dmdAplyPswd" || name === "dmdAplyPswdConfirm") + ), + ) .map(({ name, label, component }) => { const error = form.formState.errors[name]; const inputId = `${name}-input`; const errorId = `${name}-error`; - const isRequired = ["ctpvCd", "sggCd", "dmdCoNm", "dmdInfoId", "daddr", "telno", "dmdAplyPswd", "dmdAplyPswdConfirm"].includes(name); + const isRequired = [ + "ctpvCd", + "sggCd", + "dmdCoNm", + "dmdInfoId", + "daddr", + "telno", + "dmdAplyPswd", + "dmdAplyPswdConfirm", + ].includes(name); return ( (
-