Merge commit '03179ede64699cd3ccdfe56efb44ea48bcfe6cbf' into iipbs/wr/gisPopup
This commit is contained in:
@@ -35,6 +35,7 @@
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"date-fns": "^3.6.0",
|
||||
"libphonenumber-js": "^1.12.9",
|
||||
"lucide-react": "^0.474.0",
|
||||
"path": "^0.12.7",
|
||||
"react-day-picker": "^8.10.1",
|
||||
|
||||
@@ -88,12 +88,12 @@ export default function Chart({chartConfig=undefined, chartData=[], chartTitle='
|
||||
return (
|
||||
<>
|
||||
{chartTitle && chartTitle !== "" && chartTitle !== "undefined" ? (
|
||||
<div className="!mt-3 text-[2.5rem] font-bold">
|
||||
<div className="!mt-3 text-[2.5rem] font-bold text-center">
|
||||
{chartTitle}
|
||||
</div>): null}
|
||||
<div className="flex justify-center">
|
||||
<ChartContainer config={chartConfig ?? {}} className={chartClassName + " !w-[90%]"}>
|
||||
<BarChart accessibilityLayer data={chartData}>
|
||||
<div className="flex justify-center h-full w-full">
|
||||
<ChartContainer config={chartConfig ?? {}} className={chartClassName + " !w-[88%]"}>
|
||||
<BarChart accessibilityLayer data={chartData} margin={{ top: 5, right: 20, left: 30, bottom: 5 }}>
|
||||
<CartesianGrid vertical={false} />
|
||||
<XAxis
|
||||
dataKey="label"
|
||||
@@ -110,8 +110,7 @@ export default function Chart({chartConfig=undefined, chartData=[], chartTitle='
|
||||
axisLine={true}
|
||||
fontSize={12}
|
||||
fontWeight={500}
|
||||
domain={[0, Math.ceil(barMaxValue * 1)]}
|
||||
label={yFieldText ? { value: yFieldText, className:"text-[13px] font-bold", position: 'insideLeft', angle: -90, offset: 20 } : undefined}
|
||||
label={yFieldText ? { value: yFieldText, className:"text-[13px] font-bold", position: 'insideLeft', angle: -90, offset: -24, orientation: "left" } : undefined}
|
||||
/>
|
||||
<ChartTooltip cursor={false} content={<CustomTooltip active={undefined} payload={undefined} label={undefined} />}/>
|
||||
{chartData && chartData.length > 0 && (
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
} from "./lib/chart";
|
||||
import type { CSSProperties } from "react";
|
||||
|
||||
/*
|
||||
const customStyles = {
|
||||
legendContainer: {
|
||||
display: "flex",
|
||||
@@ -17,6 +18,22 @@ const customStyles = {
|
||||
marginTop: "10px",
|
||||
} as CSSProperties,
|
||||
};
|
||||
*/
|
||||
|
||||
const customStyles = {
|
||||
legendContainer: {
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
gap: "12px",
|
||||
fontSize: "13px",
|
||||
fontWeight: "normal",
|
||||
marginTop: "16px",
|
||||
marginBottom: "8px",
|
||||
minHeight: "32px",
|
||||
} as CSSProperties,
|
||||
};
|
||||
|
||||
type BarChartProps = {
|
||||
chartConfig: ChartConfig | undefined;
|
||||
@@ -60,7 +77,7 @@ const CustomTooltip = ({
|
||||
};
|
||||
|
||||
// 커스텀 범례 (글씨 크기 키우기 + 스타일 추가)
|
||||
const CustomLegend = (props: any) => {
|
||||
/*const CustomLegend = (props: any) => {
|
||||
const { payload } = props;
|
||||
return (
|
||||
<div style={customStyles.legendContainer}>
|
||||
@@ -72,6 +89,38 @@ const CustomLegend = (props: any) => {
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};*/
|
||||
const CustomLegend = (props: any) => {
|
||||
const { payload } = props;
|
||||
if (!payload) return null;
|
||||
|
||||
return (
|
||||
<div style={customStyles.legendContainer}>
|
||||
{payload.map((entry: any, index: number) => (
|
||||
<div
|
||||
key={`legend-${index}`}
|
||||
className="flex items-center"
|
||||
style={{
|
||||
minWidth: "90px",
|
||||
margin: "2px 0",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: "14px",
|
||||
height: "14px",
|
||||
backgroundColor: entry.color,
|
||||
borderRadius: "3px",
|
||||
marginRight: "8px",
|
||||
minWidth: "14px",
|
||||
minHeight: "14px",
|
||||
}}
|
||||
></div>
|
||||
<span style={{ fontSize: "13px", whiteSpace: "nowrap" }}>{entry.value}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default function DoubleBarChart({chartConfig=undefined, chartData=[], chartTitle='차트', chartClassName='w-full h-full', graphSize=40, yFieldText='', showLegend=false} : BarChartProps) {
|
||||
@@ -83,7 +132,7 @@ export default function DoubleBarChart({chartConfig=undefined, chartData=[], cha
|
||||
.map(key => item[key])
|
||||
).flat());
|
||||
};
|
||||
console.log(chartData);
|
||||
|
||||
const barMaxValue = getMaxValue(chartData, 'barVal');
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -48,6 +48,7 @@ type LegendProps = {
|
||||
};
|
||||
|
||||
/** 차트 기본 스타일 */
|
||||
/*
|
||||
const customStyles = {
|
||||
legendContainer: {
|
||||
display: "flex",
|
||||
@@ -58,6 +59,23 @@ const customStyles = {
|
||||
marginTop: "10px",
|
||||
} as CSSProperties,
|
||||
};
|
||||
*/
|
||||
|
||||
/** 차트 기본 스타일 (라인차트와 동일하게) */
|
||||
const customStyles = {
|
||||
legendContainer: {
|
||||
display: "flex",
|
||||
flexWrap: "wrap", // 여러 줄로 자동 줄바꿈
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
gap: "12px", // 아이템 간 간격
|
||||
fontSize: "13px",
|
||||
fontWeight: "normal",
|
||||
marginTop: "16px",
|
||||
marginBottom: "8px",
|
||||
minHeight: "32px", // 범례 영역 높이 통일
|
||||
} as CSSProperties,
|
||||
};
|
||||
|
||||
// 커스텀 툴팁
|
||||
const CustomTooltip = ({ active, payload, label }: {
|
||||
@@ -82,6 +100,7 @@ const CustomTooltip = ({ active, payload, label }: {
|
||||
};
|
||||
|
||||
// 커스텀 범례
|
||||
/*
|
||||
const CustomLegend = (props: LegendProps) => {
|
||||
const { payload } = props;
|
||||
if (!payload) return null;
|
||||
@@ -97,12 +116,52 @@ const CustomLegend = (props: LegendProps) => {
|
||||
</div>
|
||||
);
|
||||
};
|
||||
*/
|
||||
|
||||
const CustomLegend = (props: LegendProps) => {
|
||||
const { payload } = props;
|
||||
if (!payload) return null;
|
||||
|
||||
return (
|
||||
<div style={customStyles.legendContainer}>
|
||||
{payload.map((entry: LegendEntry, index: number) => (
|
||||
<div
|
||||
key={`legend-${index}`}
|
||||
className="flex items-center"
|
||||
style={{
|
||||
minWidth: "90px",
|
||||
margin: "2px 0",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: "14px",
|
||||
height: "14px",
|
||||
backgroundColor: entry.fill,
|
||||
borderRadius: "3px",
|
||||
marginRight: "8px",
|
||||
minWidth: "14px",
|
||||
minHeight: "14px",
|
||||
}}
|
||||
></div>
|
||||
<span style={{ fontSize: "13px", whiteSpace: "nowrap" }}>
|
||||
{entry.payload?.label || entry.value}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default function DoughnutChart(props: {
|
||||
chartConfig: any;
|
||||
chartData: ChartDataItem[] | undefined;
|
||||
chartTitle: string
|
||||
}) {
|
||||
const legendData = props.chartData?.map(item => ({
|
||||
value: item.label,
|
||||
fill: props.chartConfig?.[item.label]?.fill || 'blue',
|
||||
})) ?? [];
|
||||
return (
|
||||
<Card className="flex flex-col">
|
||||
<CardContent className="flex-1 pb-0">
|
||||
@@ -130,9 +189,9 @@ export default function DoughnutChart(props: {
|
||||
nameKey="label"
|
||||
innerRadius={60}
|
||||
/>
|
||||
<CustomLegend />
|
||||
</PieChart>
|
||||
</ChartContainer>
|
||||
<CustomLegend payload={legendData} />
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@@ -18,6 +18,7 @@ type LineChartProps = {
|
||||
}
|
||||
|
||||
/** 차트 기본 스타일 */
|
||||
/*
|
||||
const customStyles = {
|
||||
legendContainer: {
|
||||
display: "flex",
|
||||
@@ -28,6 +29,23 @@ const customStyles = {
|
||||
marginTop: "10px",
|
||||
} as CSSProperties,
|
||||
};
|
||||
*/
|
||||
|
||||
/** 차트 기본 스타일 */
|
||||
const customStyles = {
|
||||
legendContainer: {
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
gap: "12px",
|
||||
fontSize: "13px",
|
||||
fontWeight: "normal",
|
||||
marginTop: "16px",
|
||||
marginBottom: "8px",
|
||||
minHeight: "32px",
|
||||
} as CSSProperties,
|
||||
};
|
||||
|
||||
type LegendEntry = {
|
||||
value: string;
|
||||
@@ -60,7 +78,7 @@ const CustomTooltip = ({ active, payload, label }: { active?: boolean; payload?:
|
||||
};
|
||||
|
||||
// 커스텀 범례 (글씨 크기 키우기 + 스타일 추가)
|
||||
const CustomLegend = (props: LegendProps) => {
|
||||
/*const CustomLegend = (props: LegendProps) => {
|
||||
const { payload } = props;
|
||||
if (!payload) return null;
|
||||
|
||||
@@ -74,6 +92,40 @@ const CustomLegend = (props: LegendProps) => {
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};*/
|
||||
|
||||
// 커스텀 범례 (도넛차트와 동일하게)
|
||||
const CustomLegend = (props: LegendProps) => {
|
||||
const { payload } = props;
|
||||
if (!payload) return null;
|
||||
|
||||
return (
|
||||
<div style={customStyles.legendContainer}>
|
||||
{payload.map((entry: LegendEntry, index: number) => (
|
||||
<div
|
||||
key={`legend-${index}`}
|
||||
className="flex items-center"
|
||||
style={{
|
||||
minWidth: "90px",
|
||||
margin: "2px 0",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: "14px",
|
||||
height: "14px",
|
||||
backgroundColor: entry.color,
|
||||
borderRadius: "3px",
|
||||
marginRight: "8px",
|
||||
minWidth: "14px",
|
||||
minHeight: "14px",
|
||||
}}
|
||||
></div>
|
||||
<span style={{ fontSize: "13px", whiteSpace: "nowrap" }}>{entry.value}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
type DataType = {label: string; val: string};
|
||||
|
||||
@@ -17,6 +17,7 @@ type MonthPickerProps = {
|
||||
field: { value: string; onChange: (date: string) => void }; // yyyyMM 문자열
|
||||
selectClass?: string;
|
||||
readOnly?: boolean;
|
||||
startYear?: number; // 시작 연도 props 추가
|
||||
};
|
||||
|
||||
// yyyyMM → Date 객체
|
||||
@@ -37,6 +38,7 @@ export const DateMonthPicker = ({
|
||||
field,
|
||||
selectClass,
|
||||
readOnly = false,
|
||||
startYear,
|
||||
}: MonthPickerProps) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
@@ -75,6 +77,7 @@ export const DateMonthPicker = ({
|
||||
<MonthPicker
|
||||
currentMonth={parsedDate}
|
||||
onMonthChange={handleSelect}
|
||||
startYear={startYear}
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
|
||||
@@ -28,35 +28,41 @@ export type MonthPickerProps = {
|
||||
newMonth: Date | undefined,
|
||||
shouldClosePopover?: boolean
|
||||
) => void;
|
||||
startYear?: number;
|
||||
};
|
||||
|
||||
export function MonthPicker({ currentMonth, onMonthChange }: MonthPickerProps) {
|
||||
export function MonthPicker({ currentMonth, onMonthChange, startYear }: MonthPickerProps) {
|
||||
|
||||
const today = startOfToday();
|
||||
const currentYear = today.getFullYear();
|
||||
const currentYear = today.getFullYear();
|
||||
const yearFromProp = format(currentMonth, "yyyy");
|
||||
const selectedYear = parseInt(yearFromProp);
|
||||
const selectedYear = parseInt(yearFromProp);
|
||||
|
||||
// 현재 월의 시작 날짜
|
||||
const fullYearStart = parse(yearFromProp, "yyyy", new Date());
|
||||
|
||||
const yearEnd =
|
||||
selectedYear === currentYear ? today : endOfYear(fullYearStart);
|
||||
|
||||
|
||||
|
||||
|
||||
const yearRange = Array.from({ length: 21 }, (_, i) => currentYear - 20 + i);
|
||||
|
||||
|
||||
// startYear가 없으면 기존과 동일하게 (현재년도 - 20년부터 현재년도까지)
|
||||
// startYear가 있으면 해당 년도부터 현재년도까지, 내림차순으로 정렬
|
||||
const yearRange = startYear
|
||||
? Array.from({ length: currentYear - startYear + 1 }, (_, i) => currentYear - i)
|
||||
: Array.from({ length: 21 }, (_, i) => currentYear - 20 + i);
|
||||
|
||||
const months = eachMonthOfInterval({ start: fullYearStart, end: yearEnd });
|
||||
|
||||
const getMonthButtonClass = (month: Date) =>
|
||||
cn(
|
||||
"inline-flex h-9 w-16 items-center justify-center rounded-md p-0 text-sm font-normal ring-offset-white transition-colors hover:bg-slate-100 hover:text-slate-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-slate-400 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 aria-selected:opacity-100 dark:ring-offset-slate-950 dark:hover:bg-slate-800 dark:hover:text-slate-50 dark:focus-visible:ring-slate-800",
|
||||
isEqual(month, currentMonth) &&
|
||||
"bg-slate-900 text-slate-50 hover:bg-slate-900 hover:text-slate-50 focus:bg-slate-900 focus:text-slate-50 dark:bg-slate-50 dark:text-slate-900 dark:hover:bg-slate-50 dark:hover:text-slate-900 dark:focus:bg-slate-50 dark:focus:text-slate-900",
|
||||
"bg-slate-900 text-slate-50 hover:bg-slate-900 hover:text-slate-50 focus:bg-slate-900 focus:text-slate-50 dark:bg-slate-50 dark:text-slate-900 dark:hover:bg-slate-50 dark:hover:text-slate-900 dark:focus:bg-slate-50 dark:focus:text-slate-900",
|
||||
!isEqual(month, currentMonth) &&
|
||||
isEqual(month, getStartOfCurrentMonth()) &&
|
||||
"bg-slate-100 text-slate-900 dark:bg-slate-800 dark:text-slate-50"
|
||||
isEqual(month, getStartOfCurrentMonth()) &&
|
||||
"bg-slate-100 text-slate-900 dark:bg-slate-800 dark:text-slate-50"
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -77,7 +83,7 @@ const selectedYear = parseInt(yearFromProp);
|
||||
onMonthChange(newDate, false);
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="w-[120px] h-7">
|
||||
<SelectTrigger className="w-[140px] h-9 text-base">
|
||||
<SelectValue placeholder="년도" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
@@ -112,7 +118,7 @@ const selectedYear = parseInt(yearFromProp);
|
||||
onClick={() => onMonthChange(month, true)}
|
||||
>
|
||||
<time dateTime={format(month, "yyyy-MM-dd")}>
|
||||
{format(month, 'MMM', { locale: ko })}
|
||||
{format(month, 'MMM', { locale: ko })}
|
||||
</time>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader } from "@common/components";
|
||||
import { cn } from "../../lib/utils";
|
||||
|
||||
type ModalType = {
|
||||
type ModalType = {
|
||||
bodyClass: string;
|
||||
body: React.ReactNode;
|
||||
open?: boolean;
|
||||
@@ -10,43 +10,45 @@ type ModalType = {
|
||||
noFooterYn?: 'N' | 'Y';
|
||||
headerTitle?: string;
|
||||
footerContent?: React.ReactNode;
|
||||
useDirectRender?: boolean;
|
||||
}
|
||||
|
||||
export function UiModal({
|
||||
bodyClass,
|
||||
body,
|
||||
open,
|
||||
export function UiModal({
|
||||
bodyClass,
|
||||
body,
|
||||
open,
|
||||
onOpenChange,
|
||||
noHeaderYn = 'N',
|
||||
noFooterYn = 'N',
|
||||
headerTitle,
|
||||
footerContent
|
||||
footerContent,
|
||||
useDirectRender = false
|
||||
}: ModalType) {
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className={bodyClass}>
|
||||
{noHeaderYn === 'N' && (
|
||||
<DialogHeader>
|
||||
<DialogDescription>
|
||||
{headerTitle}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
)}
|
||||
{(open && body) ?
|
||||
<div> {body}</div>:<></>
|
||||
|
||||
|
||||
|
||||
}
|
||||
{noFooterYn === 'N' && (
|
||||
<DialogFooter className="sm:justify-start">
|
||||
{footerContent}
|
||||
</DialogFooter>
|
||||
)}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
{noHeaderYn === 'N' && (
|
||||
<DialogHeader>
|
||||
<DialogDescription>
|
||||
{headerTitle}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
)}
|
||||
{(open && body) ?
|
||||
useDirectRender
|
||||
? body
|
||||
: <div>{body}</div>
|
||||
: <></>
|
||||
}
|
||||
{noFooterYn === 'N' && (
|
||||
<DialogFooter className="sm:justify-start">
|
||||
{footerContent}
|
||||
</DialogFooter>
|
||||
)}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
import { LoadingWithAnimation } from '@common/components/ui/loading';
|
||||
|
||||
interface LoadingOverlayProps {
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
const LoadingOverlay: React.FC<LoadingOverlayProps> = ({ isLoading }) => {
|
||||
if (!isLoading) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50 z-50">
|
||||
<div className="relative w-16 h-16">
|
||||
<LoadingWithAnimation color="primary" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoadingOverlay;
|
||||
@@ -149,7 +149,7 @@ function Gis({
|
||||
projection: GisApp.TO_PROJ,
|
||||
zoom: gisCore.defaults.zoom,
|
||||
center: olProj.transform(
|
||||
[126.6757, 37.5585],
|
||||
[127.4875, 37.4916],
|
||||
GisApp.FROM_PROJ,
|
||||
GisApp.TO_PROJ
|
||||
),
|
||||
|
||||
@@ -187,7 +187,7 @@ export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes
|
||||
image.src = canvas.toDataURL('image/png');
|
||||
return image;
|
||||
};
|
||||
|
||||
|
||||
if (!treeData || treeData.length === 0) return null;
|
||||
|
||||
return (
|
||||
@@ -220,6 +220,7 @@ export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes
|
||||
<input type="checkbox" checked={checked.includes(node.id)} onChange={() => handleCheck(node.id)} className="cursor-pointer" />
|
||||
{node.icon && <span>{node.icon}</span>}
|
||||
<span className="text-[1.3rem] font-medium text-label">{node.text}</span>
|
||||
{depth > 0 && <span><img className="cursor-pointer w-5 h-5" src='/images/map/layericon/Layer_Select_BTN.png' onClick={()=>{}} alt={node.text}></img></span>}
|
||||
</div>
|
||||
)}
|
||||
sort={(a, b) => {
|
||||
|
||||
@@ -92,7 +92,7 @@ GisApp.core.prototype = {
|
||||
target: '',
|
||||
centerX: 37.2806094,
|
||||
centerY: 127.0032502,
|
||||
zoom: 12,
|
||||
zoom: 15,
|
||||
minZoom: 6,
|
||||
maxZoom: 20,
|
||||
},
|
||||
|
||||
@@ -3,9 +3,9 @@ import { SelectBox } from "../select";
|
||||
import { Input } from "@common/components";
|
||||
import { FormUi } from "../form_ui";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { AsYouType } from "libphonenumber-js";
|
||||
import type { ControllerRenderProps, UseFormReturn } from "react-hook-form";
|
||||
import { SdmSearchForm } from "./sdmNameBox";
|
||||
import { set } from "date-fns";
|
||||
|
||||
// 수요처 정보
|
||||
export type DemandCompType = {
|
||||
@@ -79,6 +79,19 @@ export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigun
|
||||
const [updateCoNm, setUpdateNm] = useState<string >('')
|
||||
|
||||
|
||||
|
||||
const formatPhoneNumber = (value:string) => {
|
||||
const input = value;
|
||||
const formatter = new AsYouType("KR");
|
||||
const formatted = formatter.input(input);
|
||||
|
||||
// 저장할 값 처리: 숫자만 추출
|
||||
const digitsOnly = input.replace(/\D/g, "");
|
||||
|
||||
return formatted
|
||||
};
|
||||
|
||||
|
||||
const initializeSigunData = useCallback((sidoCd: string) => {
|
||||
if (sidoCd === '0') {
|
||||
setSigunValue('0');
|
||||
@@ -325,14 +338,23 @@ export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigun
|
||||
{
|
||||
name: "telno",
|
||||
label: "전화번호",
|
||||
component: (field) => (
|
||||
component: (field) =>
|
||||
|
||||
{
|
||||
const formattedValue = formatPhoneNumber(field.value??''); // 초기 렌더링 시에도 포맷 적용
|
||||
|
||||
return (
|
||||
<Input
|
||||
placeholder="전화번호를 입력하세요"
|
||||
type="text"
|
||||
className="!text-xl text-gray-600"
|
||||
{...field}
|
||||
value={formattedValue}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value.replace(/\D/g, ''); // 숫자만 저장
|
||||
field.onChange(value);
|
||||
}}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
@@ -41,8 +41,8 @@ const createFormSchema = (isBznmTotNoDisabled: boolean, isDuplicate: boolean ,up
|
||||
telno: z.string()
|
||||
.min(8, "전화번호는 8자 이상 입력해주세요")
|
||||
.max(11, "전화번호는 11자 이내로 입력해주세요")
|
||||
.regex(/^[0-9-]+$/, "전화번호는 숫자와 하이픈(-)만 입력 가능합니다"),
|
||||
rgtr: z.string().optional(),
|
||||
.regex(/^[0-9]+$/, "전화번호는 숫자만 입력 가능합니다"),
|
||||
rgtr: z.string().optional(),
|
||||
dmdAplyPswd : updateList?.dmdInfoPk // 업데이트일경우
|
||||
? z.string().optional() // 패스워드는 선택 사항이며 길이 제한 없음
|
||||
: z.string().optional()
|
||||
|
||||
@@ -301,7 +301,7 @@ function SearchFormLeft({ isShowPart = false, children, selectSidoData = [], sel
|
||||
resetAllValues();
|
||||
// 리셋 후 트리거를 false로 설정
|
||||
if (setRtnPartValue) {
|
||||
setRtnPartValue('0');
|
||||
setRtnPartValue('A');
|
||||
}
|
||||
}
|
||||
}, [resetTrigger]);
|
||||
|
||||
@@ -110,9 +110,7 @@ export function UiTable2({
|
||||
? 'text-right'
|
||||
: head.align === 'center'
|
||||
? 'text-center'
|
||||
: typeof el[head.id] === 'number'
|
||||
? 'text-right'
|
||||
: 'text-center'
|
||||
: 'text-center'
|
||||
}
|
||||
${cellClick && idx === index ? '!font-[1000] cursor-pointer' :
|
||||
cellClick ? 'cursor-pointer' : ''}
|
||||
|
||||
@@ -82,7 +82,7 @@ export function UiTable({ tableData, headlist, pageSize, total, headName, childr
|
||||
boldArray.includes(index)
|
||||
?
|
||||
<TableCell
|
||||
className={`${head.align === 'right' ? 'text-right' : head.align === 'center' ? 'text-center' : typeof el[head.id] === 'number' ? 'text-right' : 'text-center'} ${
|
||||
className={`${head.align === 'right' ? 'text-right' : head.align === 'center' ? 'text-center' : 'text-center'} ${
|
||||
cellClick && idx === index ? '!font-[1000] cursor-pointer' :
|
||||
cellClick ? 'cursor-pointer' : ''
|
||||
} !font-bold`}
|
||||
@@ -97,7 +97,7 @@ export function UiTable({ tableData, headlist, pageSize, total, headName, childr
|
||||
</TableCell>
|
||||
:
|
||||
<TableCell
|
||||
className={`${head.align === 'right' ? 'text-right' : head.align === 'center' ? 'text-center' : typeof el[head.id] === 'number' ? 'text-right' : 'text-center'} ${
|
||||
className={`${head.align === 'right' ? 'text-right' : head.align === 'center' ? 'text-center' : 'text-center'} ${
|
||||
cellClick && idx === index ? '!font-[1000] cursor-pointer' :
|
||||
cellClick ? 'cursor-pointer' : ''
|
||||
}`}
|
||||
|
||||
@@ -40,7 +40,8 @@ export default function UiPageNations(commonPageNationsProps: CommonPageNationsP
|
||||
<PaginationItem>
|
||||
<PaginationPrevious
|
||||
href="#"
|
||||
onClick={() => {
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
if (startPage > 1) handlePageChange(startPage - 1);
|
||||
}}
|
||||
/>
|
||||
@@ -53,7 +54,7 @@ export default function UiPageNations(commonPageNationsProps: CommonPageNationsP
|
||||
<PaginationLink
|
||||
href="#"
|
||||
isActive={pageNum === pageProps.cur}
|
||||
onClick={() => handlePageChange(pageNum)}
|
||||
onClick={(e) => {e.preventDefault(); handlePageChange(pageNum)}}
|
||||
>
|
||||
{pageNum}
|
||||
</PaginationLink>
|
||||
@@ -65,7 +66,8 @@ export default function UiPageNations(commonPageNationsProps: CommonPageNationsP
|
||||
<PaginationItem>
|
||||
<PaginationNext
|
||||
href="#"
|
||||
onClick={() => {
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
if (endPage < totalPages) handlePageChange(endPage + 1);
|
||||
}}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user