Merge commit 'fc1aa4fe061fe26826292695cddbf8220861b8c1' into iipbs/wr/gisPopup
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -47,7 +47,7 @@ function Gis({
|
||||
const gisCore = useMemo(() => new GisApp.core({ targetId: mapRef.current }), []);
|
||||
const mapInstanceRef = useRef(null);
|
||||
const checkedLayerIds = useRef([]);
|
||||
const [viewMode, setViewMode] = useState('list'); // 상태로 관리
|
||||
const layerNmRef = useRef('')
|
||||
|
||||
// 팝업 데이터를 저장할 상태와 오버레이 참조를 저장할 ref 추가
|
||||
// const [facilityInfoPopupData, setFacilityInfoPopupData] = useState([]);
|
||||
@@ -85,12 +85,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -361,6 +381,7 @@ function Gis({
|
||||
setVisible={setTabVisible}
|
||||
onCheckedChange={handleCheckedLayersChange}
|
||||
onLayerIconClick={(nodeNm) => {
|
||||
layerNmRef.current = nodeNm
|
||||
setLayerNm(nodeNm); setIsOpen(true); const dummyFeature = {
|
||||
getProperties: () => ({}), // 최소 구조
|
||||
};
|
||||
@@ -376,6 +397,7 @@ function Gis({
|
||||
featureData={featureData || { getProperties: () => ({}) }}
|
||||
seletectedLayerNm={layerNm}
|
||||
onClose={closePopup} // 닫기 콜백 전달
|
||||
onFeatureSelect={handleFeatureSelect}
|
||||
/>
|
||||
)}
|
||||
{/* SearchView가 존재할 때만 렌더링 */}
|
||||
|
||||
@@ -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);
|
||||
@@ -214,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<{
|
||||
@@ -296,7 +317,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 +389,60 @@ export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigun
|
||||
{...field}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
name: "dmdAplyPswdConfirm",
|
||||
label: "패스워드확인",
|
||||
component: (field) => (
|
||||
<Input
|
||||
placeholder="패스워드를 입력하세요"
|
||||
type="password"
|
||||
className="!text-xl text-gray-600"
|
||||
{...field}
|
||||
/>
|
||||
)
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<>
|
||||
{formFields.filter((item)=>
|
||||
{if(updateList?.dmdInfoPk){
|
||||
return item.name !== 'dmdAplyPswd'
|
||||
}else{
|
||||
return item
|
||||
}
|
||||
})
|
||||
.map(({ name, label, component }) => (
|
||||
<FormField
|
||||
key={String(name)}
|
||||
control={form.control}
|
||||
name={name}
|
||||
render={({ field }) => (
|
||||
name === 'bznmTotNo' ? component(field) : (
|
||||
<FormUi label={label}>
|
||||
{component(field)}
|
||||
</FormUi>
|
||||
)
|
||||
)}
|
||||
/>
|
||||
))}
|
||||
{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 (
|
||||
<FormField
|
||||
key={String(name)}
|
||||
control={form.control}
|
||||
name={name}
|
||||
render={({ field }) => (
|
||||
<div className="mb-4">
|
||||
<label htmlFor={inputId} className="block text-gray-800 font-medium">
|
||||
{label}
|
||||
{isRequired && <span className="text-red-500 ml-1">*</span>}
|
||||
</label>
|
||||
|
||||
{React.cloneElement(component(field), {
|
||||
id: inputId,
|
||||
"aria-required": isRequired,
|
||||
"aria-invalid": !!error,
|
||||
"aria-describedby": error ? errorId : undefined
|
||||
})}
|
||||
|
||||
{error?.message && (
|
||||
<p id={errorId} role="alert" className="text-red-500 text-sm mt-1">
|
||||
{error.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -42,12 +42,24 @@ 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()
|
||||
.refine((val) => !val || (val.length >= 8 && val.length <= 16), "패스워드는 8자 이상 16자 이내로 입력해주세요"),
|
||||
|
||||
dmdAplyPswdConfirm: updateList?.dmdInfoPk // 업데이트일경우
|
||||
? z.string().optional() // 패스워드는 선택 사항이며 길이 제한 없음
|
||||
: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<ReturnType<typeof createFormSchema>>;
|
||||
@@ -62,6 +74,7 @@ const defaultValues: FormSchema = {
|
||||
telno: '',
|
||||
rgtr: '',
|
||||
dmdAplyPswd: '',
|
||||
dmdAplyPswdConfirm: '',
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -149,7 +162,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()}`);
|
||||
|
||||
@@ -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
|
||||
?
|
||||
<TableCell
|
||||
className={`${head.align === 'right' ? 'text-right' : head.align === 'center' ? 'text-center' : 'text-center'} ${cellClick && idx === index ? '!font-[1000] cursor-pointer' :
|
||||
cellClick ? 'cursor-pointer' : ''
|
||||
cellClick ? 'cursor-pointer' : ''
|
||||
} !font-bold`}
|
||||
onClick={() => handleCellClick(index, head.id)}
|
||||
key={`cell-${head.id}-${index}`}
|
||||
@@ -97,7 +103,7 @@ export function UiTable({ tableData, headlist, pageSize, total, headName, childr
|
||||
:
|
||||
<TableCell
|
||||
className={`${head.align === 'right' ? 'text-right' : head.align === 'center' ? 'text-center' : 'text-center'} ${cellClick && idx === index ? '!font-[1000] cursor-pointer' :
|
||||
cellClick ? 'cursor-pointer' : ''
|
||||
cellClick ? 'cursor-pointer' : ''
|
||||
}`}
|
||||
onClick={() => handleCellClick(index, head.id)}
|
||||
key={`cell-${head.id}-${index}`}
|
||||
|
||||
@@ -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<typeof DayPicker>;
|
||||
|
||||
export function DatePicker({
|
||||
className,
|
||||
placeholder = "날짜 선택",
|
||||
disabled,
|
||||
zIndex, // zIndex prop 받기
|
||||
...props
|
||||
}: DatePickerProps) {
|
||||
return (
|
||||
@@ -36,8 +39,11 @@ export function DatePicker({
|
||||
)}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0">
|
||||
<Calendar initialFocus classNames={{ cell: "rounded-md" }} {...props} />
|
||||
<PopoverContent
|
||||
className="w-auto p-0"
|
||||
style={{ zIndex: zIndex || 'auto' }}
|
||||
>
|
||||
<Calendar classNames={{ cell: "rounded-md" }} {...props} />
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user