From a5aeeb3d40d48505d6bdcc152f505a3e9b5c1f21 Mon Sep 17 00:00:00 2001 From: leedj312 Date: Sat, 28 Jun 2025 15:00:43 +0900 Subject: [PATCH 1/6] =?UTF-8?q?datemonthPicker=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/ui/date_month_picker/index.tsx | 82 ++++++++++++ .../src/ui/date_month_picker/mothPicker.tsx | 125 ++++++++++++++++++ .../business_components/src/ui/index.ts | 3 +- 3 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 packages/common/business_components/src/ui/date_month_picker/index.tsx create mode 100644 packages/common/business_components/src/ui/date_month_picker/mothPicker.tsx diff --git a/packages/common/business_components/src/ui/date_month_picker/index.tsx b/packages/common/business_components/src/ui/date_month_picker/index.tsx new file mode 100644 index 000000000..d318b561f --- /dev/null +++ b/packages/common/business_components/src/ui/date_month_picker/index.tsx @@ -0,0 +1,82 @@ +'use client'; + +import { cn } from "../../lib/utils"; +import { + Popover, + PopoverTrigger, + PopoverContent, + Button, +} from "@common/components/ui"; +import { ko } from "date-fns/locale"; +import { format } from "date-fns"; +import { useState } from "react"; +import { MonthPicker } from "./mothPicker"; +import { CalendarIcon } from "lucide-react"; + +type MonthPickerProps = { + field: { value: string; onChange: (date: string) => void }; // yyyyMM 문자열 + selectClass?: string; + readOnly?: boolean; +}; + +// yyyyMM → Date 객체 +const parseYYYYMM = (value: string) => { + const year = Number(value.substring(0, 4)); + const month = Number(value.substring(4, 6)) - 1; + return new Date(year, month); +}; + +// Date 객체 → yyyyMM 문자열 +const formatDateToYYYYMM = (date: Date) => { + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, "0"); + return `${year}${month}`; +}; + +export const DateMonthPicker = ({ + field, + selectClass, + readOnly = false, +}: MonthPickerProps) => { + const [open, setOpen] = useState(false); + + const parsedDate = field?.value ? parseYYYYMM(field.value) : new Date(); + + const handleSelect = ( + date: Date | undefined, + shouldClosePopover?: boolean + ) => { + if (!readOnly && date) { + field.onChange(formatDateToYYYYMM(date)); + if (shouldClosePopover) setOpen(false); + } + }; + + return ( + + + + + + + + + ); +}; diff --git a/packages/common/business_components/src/ui/date_month_picker/mothPicker.tsx b/packages/common/business_components/src/ui/date_month_picker/mothPicker.tsx new file mode 100644 index 000000000..acf17fba4 --- /dev/null +++ b/packages/common/business_components/src/ui/date_month_picker/mothPicker.tsx @@ -0,0 +1,125 @@ +import { cn } from "../../lib/utils"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@common/components/ui"; +import { + eachMonthOfInterval, + endOfYear, + format, + isEqual, + isFuture, + parse, + startOfMonth, + startOfToday, +} from "date-fns"; +import { ko } from "date-fns/locale"; + +function getStartOfCurrentMonth() { + return startOfMonth(startOfToday()); +} + +export type MonthPickerProps = { + currentMonth: Date; + onMonthChange: ( + newMonth: Date | undefined, + shouldClosePopover?: boolean + ) => void; +}; + +export function MonthPicker({ currentMonth, onMonthChange }: MonthPickerProps) { + + const today = startOfToday(); +const currentYear = today.getFullYear(); + const yearFromProp = format(currentMonth, "yyyy"); +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); + 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", + !isEqual(month, currentMonth) && + isEqual(month, getStartOfCurrentMonth()) && + "bg-slate-100 text-slate-900 dark:bg-slate-800 dark:text-slate-50" + ); + + return ( +
+
+
+ {/* 년도 셀렉트 */} +
+ +
+ + {/* 월 그리드 */} +
+ {months.map((month) => ( +
+ +
+ ))} +
+
+
+
+ ); +} diff --git a/packages/common/business_components/src/ui/index.ts b/packages/common/business_components/src/ui/index.ts index 19d966a53..fd1e6fe9e 100644 --- a/packages/common/business_components/src/ui/index.ts +++ b/packages/common/business_components/src/ui/index.ts @@ -20,4 +20,5 @@ export * from "./table"; export * from "./table_div"; export * from "./title"; export * from "./sdmInsertForm"; -export * from "./popover_ui" \ No newline at end of file +export * from "./popover_ui"; +export * from "./date_month_picker"; \ No newline at end of file From dc6348e4f9db01708f445decd46063c9dd8225b6 Mon Sep 17 00:00:00 2001 From: leedj312 Date: Sat, 28 Jun 2025 16:20:15 +0900 Subject: [PATCH 2/6] =?UTF-8?q?=EB=AA=A8=EB=8B=AC=EC=95=88=EC=97=90=20div?= =?UTF-8?q?=20=EA=B0=90=EC=8B=B8=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../business_components/src/ui/dialog_ui/index.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/common/business_components/src/ui/dialog_ui/index.tsx b/packages/common/business_components/src/ui/dialog_ui/index.tsx index b9e341279..bbb27d327 100644 --- a/packages/common/business_components/src/ui/dialog_ui/index.tsx +++ b/packages/common/business_components/src/ui/dialog_ui/index.tsx @@ -1,4 +1,5 @@ import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader } from "@common/components"; +import { cn } from "../../lib/utils"; type ModalType = { bodyClass: string; @@ -25,15 +26,20 @@ export function UiModal({ return ( <> - - {noHeaderYn === 'N' && ( + + {noHeaderYn === 'N' && ( {headerTitle} )} - {(open && body)} + {(open && body) ? +
{body}
:<> + + + + } {noFooterYn === 'N' && ( {footerContent} From 0dcbe8549caa13decc9721b0f51490ac273dea19 Mon Sep 17 00:00:00 2001 From: leedj312 Date: Tue, 1 Jul 2025 10:28:48 +0900 Subject: [PATCH 3/6] =?UTF-8?q?libphonenumber=20=EB=9D=BC=EC=9D=B4?= =?UTF-8?q?=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20?= =?UTF-8?q?=EC=A0=84=ED=99=94=EB=B2=88=ED=98=B8=20=EC=9E=85=EB=A0=A5=20?= =?UTF-8?q?=ED=95=84=EB=93=9C=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/package.json | 1 + .../src/ui/sdmInsertForm/form.tsx | 30 ++++++++++++++++--- .../src/ui/sdmInsertForm/index.tsx | 4 +-- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/packages/common/business_components/package.json b/packages/common/business_components/package.json index e7aae9fc5..da6f8c344 100644 --- a/packages/common/business_components/package.json +++ b/packages/common/business_components/package.json @@ -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", diff --git a/packages/common/business_components/src/ui/sdmInsertForm/form.tsx b/packages/common/business_components/src/ui/sdmInsertForm/form.tsx index 6ccaac946..2d7d2bf90 100644 --- a/packages/common/business_components/src/ui/sdmInsertForm/form.tsx +++ b/packages/common/business_components/src/ui/sdmInsertForm/form.tsx @@ -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('') + + 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 ( { + const value = e.target.value.replace(/\D/g, ''); // 숫자만 저장 + field.onChange(value); + }} /> - ) + )} }, { diff --git a/packages/common/business_components/src/ui/sdmInsertForm/index.tsx b/packages/common/business_components/src/ui/sdmInsertForm/index.tsx index 13e461910..4219602e6 100644 --- a/packages/common/business_components/src/ui/sdmInsertForm/index.tsx +++ b/packages/common/business_components/src/ui/sdmInsertForm/index.tsx @@ -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() From 557cb7c882221a41323874078eec85f6595574d7 Mon Sep 17 00:00:00 2001 From: swcho Date: Tue, 1 Jul 2025 17:18:59 +0900 Subject: [PATCH 4/6] =?UTF-8?q?feat:=20SN(=ED=95=98=EC=88=98=EA=B4=80?= =?UTF-8?q?=EB=A1=9C)=20GIS=20=EA=B3=B5=ED=86=B5=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=BB=A4=EB=B0=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../business_components/src/ui/map/Gis.js | 19 +++++++++---------- .../src/ui/map/lib/gis.core.js | 2 +- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/packages/common/business_components/src/ui/map/Gis.js b/packages/common/business_components/src/ui/map/Gis.js index 8b8acf1cb..fa1cf7ab4 100644 --- a/packages/common/business_components/src/ui/map/Gis.js +++ b/packages/common/business_components/src/ui/map/Gis.js @@ -1,20 +1,19 @@ -import './lib/css/gis.common.css'; -import React, { createContext, useContext, useEffect, useMemo, useRef, useState, useCallback } from 'react'; +import { Search } from 'lucide-react'; import OlMap from 'ol/Map'; import View from 'ol/View'; import { defaults as defaultControls, OverviewMap } from 'ol/control'; import { defaults } from 'ol/interaction'; import * as olProj from 'ol/proj'; -import proj4 from 'proj4'; import { register } from 'ol/proj/proj4'; -import GisApp from './lib/gis.core'; -import GisContent from './GisContent'; -import GisTree from './GisTree'; +import proj4 from 'proj4'; +import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'; import GisLonLat from './GisLonLat'; +import GisMapToggle from './GisMapToggle'; import GisSelect from './GisSelect'; import GisToolBar from './GisToolBar'; -import GisMapToggle from './GisMapToggle'; -import { Search } from 'lucide-react'; +import GisTree from './GisTree'; +import './lib/css/gis.common.css'; +import GisApp from './lib/gis.core'; export const GisContext = createContext({ gisCore: null, gisMap: null, treeData: null, checkedLayerIds: null }); export const useGisContext = () => useContext(GisContext); @@ -122,7 +121,7 @@ function Gis({ isOpen , gisMaps, isSearchModalOpen, treeData = [], isSearch = fa 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 ), @@ -242,4 +241,4 @@ function Gis({ isOpen , gisMaps, isSearchModalOpen, treeData = [], isSearch = fa ); } -export default React.memo(Gis); \ No newline at end of file +export default React.memo(Gis); diff --git a/packages/common/business_components/src/ui/map/lib/gis.core.js b/packages/common/business_components/src/ui/map/lib/gis.core.js index db58c898f..ddfcec3a1 100644 --- a/packages/common/business_components/src/ui/map/lib/gis.core.js +++ b/packages/common/business_components/src/ui/map/lib/gis.core.js @@ -94,7 +94,7 @@ GisApp.core.prototype = { target: '', centerX: 37.2806094, centerY: 127.0032502, - zoom: 12, + zoom: 15, minZoom: 6, maxZoom: 20, }, From 031307aa182af9c811ecb31eabdb90cc2716f8ef Mon Sep 17 00:00:00 2001 From: YangJinSoo94 Date: Wed, 2 Jul 2025 15:14:04 +0900 Subject: [PATCH 5/6] =?UTF-8?q?searchFormLeft=20=EC=B4=88=EA=B8=B0?= =?UTF-8?q?=ED=99=94=20=EC=8B=9C=20A=EB=A1=9C=20=EC=B4=88=EA=B8=B0?= =?UTF-8?q?=ED=99=94=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../business_components/src/ui/search_form_left/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/common/business_components/src/ui/search_form_left/index.tsx b/packages/common/business_components/src/ui/search_form_left/index.tsx index e7858df2a..d0da2cd1a 100644 --- a/packages/common/business_components/src/ui/search_form_left/index.tsx +++ b/packages/common/business_components/src/ui/search_form_left/index.tsx @@ -301,7 +301,7 @@ function SearchFormLeft({ isShowPart = false, children, selectSidoData = [], sel resetAllValues(); // 리셋 후 트리거를 false로 설정 if (setRtnPartValue) { - setRtnPartValue('0'); + setRtnPartValue('A'); } } }, [resetTrigger]); From b657ee97eb0ced68807f6816e0fc78faabcea479 Mon Sep 17 00:00:00 2001 From: swcho Date: Wed, 2 Jul 2025 19:21:52 +0900 Subject: [PATCH 6/6] =?UTF-8?q?feat:=20UITable,=20UITable2=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=88=AB=EC=9E=90=20=EA=B0=80?= =?UTF-8?q?=EC=9A=B4=EB=8D=B0=20=EC=A0=95=EB=A0=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/common/business_components/src/ui/table/index-2.tsx | 4 +--- packages/common/business_components/src/ui/table/index.tsx | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/common/business_components/src/ui/table/index-2.tsx b/packages/common/business_components/src/ui/table/index-2.tsx index b6a9750b0..5961ea396 100644 --- a/packages/common/business_components/src/ui/table/index-2.tsx +++ b/packages/common/business_components/src/ui/table/index-2.tsx @@ -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' : ''} diff --git a/packages/common/business_components/src/ui/table/index.tsx b/packages/common/business_components/src/ui/table/index.tsx index cd25bc8db..bb90153c2 100644 --- a/packages/common/business_components/src/ui/table/index.tsx +++ b/packages/common/business_components/src/ui/table/index.tsx @@ -82,7 +82,7 @@ export function UiTable({ tableData, headlist, pageSize, total, headName, childr boldArray.includes(index) ? :