From c1d741dd498e44be8887cca2d536caada510fea8 Mon Sep 17 00:00:00 2001 From: swcho Date: Tue, 8 Jul 2025 11:23:01 +0900 Subject: [PATCH 1/7] =?UTF-8?q?feat:=20table=20Data=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=EC=8B=9C=20=EC=85=80=20=ED=81=B4=EB=A6=AD=20=EC=B4=88=EA=B8=B0?= =?UTF-8?q?=ED=99=94=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/business_components/src/ui/table/index.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/common/business_components/src/ui/table/index.tsx b/packages/common/business_components/src/ui/table/index.tsx index fe720abc0..01bb18ba2 100644 --- a/packages/common/business_components/src/ui/table/index.tsx +++ b/packages/common/business_components/src/ui/table/index.tsx @@ -49,6 +49,12 @@ export function UiTable({ tableData, headlist, pageSize, total, headName, childr } }, [resetSelection]); + // 테이블 데이터가 변경될 때마다 선택된 행 초기화하는 useEffect 추가 + useEffect(() => { + // 새로운 데이터가 로드되면 선택 상태 초기화 + setIdx(-1); + }, [tableData]); + const handlePageChange = (page: number) => { setCur(page); onPageChange(page); @@ -83,7 +89,7 @@ export function UiTable({ tableData, headlist, pageSize, total, headName, childr ? handleCellClick(index, head.id)} key={`cell-${head.id}-${index}`} @@ -97,7 +103,7 @@ export function UiTable({ tableData, headlist, pageSize, total, headName, childr : handleCellClick(index, head.id)} key={`cell-${head.id}-${index}`} From 5ed4e0d48d3dceb8bc0e321428d632fd84308fa2 Mon Sep 17 00:00:00 2001 From: YangJinSoo94 Date: Tue, 8 Jul 2025 11:40:18 +0900 Subject: [PATCH 2/7] =?UTF-8?q?=EA=B2=B0=ED=95=A8=EC=A1=B0=EC=B9=98=20-=20?= =?UTF-8?q?=EC=88=98=EC=9A=94=EC=B2=98=EC=A0=95=EB=B3=B4=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D,=20=EB=B0=94=EC=B0=A8=ED=8A=B8=20=EC=83=89=EC=83=81?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/ui/chart/BarChart.tsx | 2 +- .../src/ui/sdmInsertForm/form.tsx | 104 +++++++++++++----- .../src/ui/sdmInsertForm/index.tsx | 15 ++- 3 files changed, 90 insertions(+), 31 deletions(-) diff --git a/packages/common/business_components/src/ui/chart/BarChart.tsx b/packages/common/business_components/src/ui/chart/BarChart.tsx index 0802a72e4..02d3a7c60 100644 --- a/packages/common/business_components/src/ui/chart/BarChart.tsx +++ b/packages/common/business_components/src/ui/chart/BarChart.tsx @@ -75,7 +75,7 @@ const CustomLegend = (props: any) => { }; export default function Chart({chartConfig=undefined, chartData=[], chartTitle='차트', chartClassName='w-full h-full', graphSize=40, yFieldText='', showLegend=false} : BarChartProps) { - const colors = ["#FF6B6B", "#4ECDC4", "#45B7D1", "#96CEB4", "#FFEEAD", "#D4A5A5"]; + const colors = ["#FF6B6B", "#4ECDC4", "#45B7D1", "#FED16A", "#FFEEAD", "#D4A5A5"]; const getMaxValue = (data: any[], keyPrefix: string) => { return Math.max(...data.map(item => Object.keys(item) diff --git a/packages/common/business_components/src/ui/sdmInsertForm/form.tsx b/packages/common/business_components/src/ui/sdmInsertForm/form.tsx index 2d7d2bf90..a42dea012 100644 --- a/packages/common/business_components/src/ui/sdmInsertForm/form.tsx +++ b/packages/common/business_components/src/ui/sdmInsertForm/form.tsx @@ -1,4 +1,5 @@ -import { FormField } from "../form"; +import React from 'react'; +import { FormField } from "../form"; import { SelectBox } from "../select"; import { Input } from "@common/components"; import { FormUi } from "../form_ui"; @@ -55,6 +56,7 @@ interface FormSchema { bznmTotNo?: string; rgtr?: string; dmdAplyPswd?: string; + dmdAplyPswdConfirm?: string; } interface SdmRegFormProps { @@ -85,12 +87,27 @@ export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigun const formatter = new AsYouType("KR"); const formatted = formatter.input(input); - // 저장할 값 처리: 숫자만 추출 - const digitsOnly = input.replace(/\D/g, ""); - 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; + } else if (limitedDigits.length <= 5) { + return `${limitedDigits.slice(0, 3)}-${limitedDigits.slice(3)}`; + } else { + return `${limitedDigits.slice(0, 3)}-${limitedDigits.slice(3, 5)}-${limitedDigits.slice(5)}`; + } + }; + const initializeSigunData = useCallback((sidoCd: string) => { if (sidoCd === '0') { @@ -179,12 +196,15 @@ export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigun const value = e.target.value; if(noType === 'dmdInfoPk') { - if(orgBznmNo.substring(0,10) === value) { + // 숫자만 추출하여 저장 (포맷팅 제거) + const digitsOnly = value.replace(/\D/g, ""); + + if(orgBznmNo.substring(0,10) === digitsOnly) { setIsDuplicate(true); } else { setIsDuplicate(false); } - field?.onChange(value); + field?.onChange(digitsOnly); } else { if(orgBznmNo.substring(10,14) === value) { setIsDuplicate(true); @@ -296,7 +316,7 @@ export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigun type="text" className="!text-xl text-gray-600" onChange={(e) => bznNoChange(e, 'dmdInfoPk', field)} - value={field.value} + value={formatBusinessNumber(field.value || '')} readOnly={!!updateList?.dmdInfoPk} /> ) @@ -368,32 +388,60 @@ export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigun {...field} /> ) + }, + { + name: "dmdAplyPswdConfirm", + label: "패스워드확인", + component: (field) => ( + + ) } ] return ( <> - {formFields.filter((item)=> - {if(updateList?.dmdInfoPk){ - return item.name !== 'dmdAplyPswd' - }else{ - return item - } - }) - .map(({ name, label, component }) => ( - ( - name === 'bznmTotNo' ? component(field) : ( - - {component(field)} - - ) - )} - /> - ))} + {formFields + .filter(({ name }) => !(updateList?.dmdInfoPk && (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", "dmdInfoPk", "daddr", "telno", "dmdAplyPswd", "dmdAplyPswdConfirm"].includes(name); + + return ( + ( +
+ + + {React.cloneElement(component(field), { + id: inputId, + "aria-required": isRequired, + "aria-invalid": !!error, + "aria-describedby": error ? errorId : undefined + })} + + {error?.message && ( + + )} +
+ )} + /> + ); + })} ); } \ No newline at end of file diff --git a/packages/common/business_components/src/ui/sdmInsertForm/index.tsx b/packages/common/business_components/src/ui/sdmInsertForm/index.tsx index 4219602e6..c564b63dd 100644 --- a/packages/common/business_components/src/ui/sdmInsertForm/index.tsx +++ b/packages/common/business_components/src/ui/sdmInsertForm/index.tsx @@ -47,7 +47,17 @@ const createFormSchema = (isBznmTotNoDisabled: boolean, isDuplicate: boolean ,up ? z.string().optional() // 패스워드는 선택 사항이며 길이 제한 없음 : z.string().optional() .refine((val) => !val || (val.length >= 8 && val.length <= 16), "패스워드는 8자 이상 16자 이내로 입력해주세요"), - + dmdAplyPswdConfirm: z.string() + .min(1, "패스워드확인을 입력해주세요"), +}).refine((data) => { + // 패스워드 확인 검증 + if (data.dmdAplyPswd && data.dmdAplyPswdConfirm) { + return data.dmdAplyPswd === data.dmdAplyPswdConfirm; + } + return true; +}, { + message: "패스워드가 일치하지 않습니다", + path: ["dmdAplyPswdConfirm"], // 에러를 패스워드 확인 필드에 표시 }); type FormSchema = z.infer>; @@ -62,6 +72,7 @@ const defaultValues: FormSchema = { telno: '', rgtr: '', dmdAplyPswd: '', + dmdAplyPswdConfirm: '', }; /** @@ -149,7 +160,7 @@ export default function SdmInsertForm({selectSidoData, selectSigunData, dataList console.log(' formData ', formData) /* await insertSdmData(formData); */ - rtnInputData(formData as DemandCompType); + rtnInputData(formData as unknown as DemandCompType); //router.push(`/pages/sdmMng/sdmInfoInsert?${queryParams.toString()}`); From 52a5650ca593c82790d8d34d5f38293797f41739 Mon Sep 17 00:00:00 2001 From: swcho Date: Tue, 8 Jul 2025 19:20:04 +0900 Subject: [PATCH 3/7] =?UTF-8?q?feat:=20DatePicker=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20zIndex=20props=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/src/ui/date-picker/index.tsx | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/common/components/src/ui/date-picker/index.tsx b/packages/common/components/src/ui/date-picker/index.tsx index ee5d37872..67265df0e 100644 --- a/packages/common/components/src/ui/date-picker/index.tsx +++ b/packages/common/components/src/ui/date-picker/index.tsx @@ -2,7 +2,7 @@ import "./date-picker.css"; import { format } from "date-fns"; import { CalendarIcon } from "lucide-react"; -import type { DayPickerSingleProps } from "react-day-picker"; +import type { DayPicker } from "react-day-picker"; import { cn } from "../../lib/utils"; import { Button } from "../button"; @@ -12,12 +12,15 @@ import { Popover, PopoverContent, PopoverTrigger } from "../popover"; type DatePickerProps = { placeholder?: string; disabled?: boolean; -} & DayPickerSingleProps; + selected?: Date; + zIndex?: number; // z-index prop 추가 +} & React.ComponentProps; export function DatePicker({ className, placeholder = "날짜 선택", disabled, + zIndex, // zIndex prop 받기 ...props }: DatePickerProps) { return ( @@ -36,8 +39,13 @@ export function DatePicker({ )} - - + + ); From 4224375b3a5aa1e3213b96ccfc3fff067a9ce06a Mon Sep 17 00:00:00 2001 From: swcho Date: Tue, 8 Jul 2025 19:31:51 +0900 Subject: [PATCH 4/7] =?UTF-8?q?feat:=20DatePicker=20zIndex=20props=20?= =?UTF-8?q?=EB=8B=A4=EC=8B=9C=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/common/components/src/ui/date-picker/index.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/common/components/src/ui/date-picker/index.tsx b/packages/common/components/src/ui/date-picker/index.tsx index 67265df0e..85c584e5f 100644 --- a/packages/common/components/src/ui/date-picker/index.tsx +++ b/packages/common/components/src/ui/date-picker/index.tsx @@ -40,10 +40,8 @@ export function DatePicker({ From bf241073b02582908ff7a2f3cbb21a1be307d25a Mon Sep 17 00:00:00 2001 From: leedj312 Date: Wed, 9 Jul 2025 01:36:19 +0900 Subject: [PATCH 5/7] =?UTF-8?q?=EC=88=98=EC=9A=94=EC=B2=98=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=EC=8B=9C=20=EC=98=88=EC=99=B8=EC=82=AC=ED=95=AD=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80(=ED=8C=A8=EC=8A=A4=EC=9B=8C=EB=93=9C=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8=EB=9E=80)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/business_components/src/ui/sdmInsertForm/form.tsx | 1 + .../common/business_components/src/ui/sdmInsertForm/index.tsx | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/common/business_components/src/ui/sdmInsertForm/form.tsx b/packages/common/business_components/src/ui/sdmInsertForm/form.tsx index a42dea012..e58592837 100644 --- a/packages/common/business_components/src/ui/sdmInsertForm/form.tsx +++ b/packages/common/business_components/src/ui/sdmInsertForm/form.tsx @@ -234,6 +234,7 @@ export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigun }, [updateList,form]); useEffect(() => { + console.log( 'form.formState.errors ', form.formState.errors) },[form.formState.errors]) const formFields: Array<{ diff --git a/packages/common/business_components/src/ui/sdmInsertForm/index.tsx b/packages/common/business_components/src/ui/sdmInsertForm/index.tsx index c564b63dd..9b46cc653 100644 --- a/packages/common/business_components/src/ui/sdmInsertForm/index.tsx +++ b/packages/common/business_components/src/ui/sdmInsertForm/index.tsx @@ -47,7 +47,9 @@ const createFormSchema = (isBznmTotNoDisabled: boolean, isDuplicate: boolean ,up ? z.string().optional() // 패스워드는 선택 사항이며 길이 제한 없음 : z.string().optional() .refine((val) => !val || (val.length >= 8 && val.length <= 16), "패스워드는 8자 이상 16자 이내로 입력해주세요"), - dmdAplyPswdConfirm: z.string() + dmdAplyPswdConfirm: updateList?.dmdInfoPk // 업데이트일경우 + ? z.string().optional() // 패스워드는 선택 사항이며 길이 제한 없음 + :z.string() .min(1, "패스워드확인을 입력해주세요"), }).refine((data) => { // 패스워드 확인 검증 From 4745c87ab0c3ea541d706012d6cbed9b24f3a98d Mon Sep 17 00:00:00 2001 From: leedj312 Date: Wed, 9 Jul 2025 01:44:17 +0900 Subject: [PATCH 6/7] =?UTF-8?q?=EC=88=98=EC=9A=94=EC=B2=98=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=5F=20rgtr=20nullsh=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/business_components/src/ui/sdmInsertForm/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/common/business_components/src/ui/sdmInsertForm/index.tsx b/packages/common/business_components/src/ui/sdmInsertForm/index.tsx index 9b46cc653..35c609f9e 100644 --- a/packages/common/business_components/src/ui/sdmInsertForm/index.tsx +++ b/packages/common/business_components/src/ui/sdmInsertForm/index.tsx @@ -42,7 +42,7 @@ const createFormSchema = (isBznmTotNoDisabled: boolean, isDuplicate: boolean ,up .min(8, "전화번호는 8자 이상 입력해주세요") .max(11, "전화번호는 11자 이내로 입력해주세요") .regex(/^[0-9]+$/, "전화번호는 숫자만 입력 가능합니다"), - rgtr: z.string().optional(), + rgtr: z.string().nullish(), dmdAplyPswd : updateList?.dmdInfoPk // 업데이트일경우 ? z.string().optional() // 패스워드는 선택 사항이며 길이 제한 없음 : z.string().optional() From fc1aa4fe061fe26826292695cddbf8220861b8c1 Mon Sep 17 00:00:00 2001 From: JIeunhuh Date: Wed, 9 Jul 2025 11:45:53 +0900 Subject: [PATCH 7/7] =?UTF-8?q?=ED=98=84=ED=99=A9=20=EC=98=A4=ED=94=88=20?= =?UTF-8?q?=EC=8B=9C=20=EB=A0=88=EC=9D=B4=EC=96=B4=EB=AA=85=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=A5=B8=20featureid=20=EA=B5=AC=EB=B6=84=20=EC=9E=91?= =?UTF-8?q?=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../business_components/src/ui/map/Gis.js | 32 ++++++++++++++++--- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/packages/common/business_components/src/ui/map/Gis.js b/packages/common/business_components/src/ui/map/Gis.js index ef5c21d5b..880c116cd 100644 --- a/packages/common/business_components/src/ui/map/Gis.js +++ b/packages/common/business_components/src/ui/map/Gis.js @@ -46,7 +46,7 @@ function Gis({ const gisCore = useMemo(() => new GisApp.core({ targetId: mapRef.current }), []); const mapInstanceRef = useRef(null); const checkedLayerIds = useRef([]); - + const layerNmRef = useRef('') // 팝업 데이터를 저장할 상태와 오버레이 참조를 저장할 ref 추가 // const [facilityInfoPopupData, setFacilityInfoPopupData] = useState([]); @@ -84,12 +84,32 @@ function Gis({ foundFeature = data; } else { // 아니면 featureId로 맵에서 Feature 인스턴스를 찾아서 사용 - const featureId = data.fid || data.ftrIdn || data.id; + let featureId + const layerName = layerNmRef.current + if (layerName === '하수재이용시설') { + featureId = data.searchPk; + } else if (layerName === '빗물이용시설') { + featureId = data.bdstInfoPk; + } else if (layerName === '하수처리시설') { + featureId = data.bscFctCd; + } else if (layerName === '중수도시설' || layerName == '온배수시설') { + featureId = data.bdstRuseFcltPk; + } else { + console.warn('해당 레이어에 대한 featureId 매핑 없음', layerNm); + featureId = data.fid || data.id || data.ftrIdn; + } gisMap.getLayers().getArray().forEach(layer => { const source = layer.getSource?.(); - if (source?.getFeatureById) { - const feature = source.getFeatureById(String(featureId).trim()); - if (feature) foundFeature = feature; + if (source?.getFeatures) { + const allFeatures = source.getFeatures(); + for (const f of allFeatures) { + const id = String(f.getId()); + // console.log('id', id, featureId) + if (id && id.split('.')[1] === String(featureId)) { + foundFeature = f; + break; + } + } } }); } @@ -360,6 +380,7 @@ function Gis({ setVisible={setTabVisible} onCheckedChange={handleCheckedLayersChange} onLayerIconClick={(nodeNm) => { + layerNmRef.current = nodeNm setLayerNm(nodeNm); setIsOpen(true); const dummyFeature = { getProperties: () => ({}), // 최소 구조 }; @@ -375,6 +396,7 @@ function Gis({ featureData={featureData || { getProperties: () => ({}) }} seletectedLayerNm={layerNm} onClose={closePopup} // 닫기 콜백 전달 + onFeatureSelect={handleFeatureSelect} /> )} {/* SearchView가 존재할 때만 렌더링 */}