diff --git a/packages/common/business_components/src/ui/chart/BarChart.tsx b/packages/common/business_components/src/ui/chart/BarChart.tsx index 218ebac34..79aa61c98 100644 --- a/packages/common/business_components/src/ui/chart/BarChart.tsx +++ b/packages/common/business_components/src/ui/chart/BarChart.tsx @@ -6,6 +6,7 @@ import { type ChartConfig } from "./lib/chart"; import type { CSSProperties } from "react"; +import { formatCommaNumber } from "./lib/function"; const customStyles = { legendContainer: { @@ -119,6 +120,7 @@ export default function Chart({chartConfig=undefined, chartData=[], chartTitle=' fontWeight={500} domain={[0, globalMax]} ticks={ticks} + tickFormatter={(value) => formatCommaNumber(value)} label={yFieldText ? { value: yFieldText, className:"text-[15px] font-bold", position: 'insideLeft', angle: -90, offset: -30 } : undefined} /> }/> diff --git a/packages/common/business_components/src/ui/chart/DoughnutChart.tsx b/packages/common/business_components/src/ui/chart/DoughnutChart.tsx index 6f113bee3..ec8fc2fd9 100644 --- a/packages/common/business_components/src/ui/chart/DoughnutChart.tsx +++ b/packages/common/business_components/src/ui/chart/DoughnutChart.tsx @@ -50,7 +50,7 @@ const customStyles = { fontWeight: "normal", marginTop: "16px", marginBottom: "8px", - minHeight: "32px", // 범례 영역 높이 통일 + minHeight: "60px", // 범례 영역 높이 통일 } as CSSProperties, }; const renderCustomizedLabel = ({ diff --git a/packages/common/business_components/src/ui/chart/lib/function.tsx b/packages/common/business_components/src/ui/chart/lib/function.tsx new file mode 100644 index 000000000..dc1921f0a --- /dev/null +++ b/packages/common/business_components/src/ui/chart/lib/function.tsx @@ -0,0 +1,12 @@ +// 숫자입력시 , 삽입 +export const formatCommaNumber = ( + value: number | string | undefined | null +): string => { + if (value === undefined || value === null || value === "") return ""; + const num = Number(value); + // 숫자로 변환할 수 없는 값이 들어올 경우 빈 문자열을 반환하여 예상치 못한 "NaN" 출력을 방지합니다. + if (isNaN(num)) { + return ""; + } + return num.toLocaleString(); +}; \ No newline at end of file diff --git a/packages/common/business_components/src/ui/date_picker/index.tsx b/packages/common/business_components/src/ui/date_picker/index.tsx index 2652eabd7..34760c245 100644 --- a/packages/common/business_components/src/ui/date_picker/index.tsx +++ b/packages/common/business_components/src/ui/date_picker/index.tsx @@ -108,12 +108,11 @@ export function DatePicker({ // captionLayout="dropdown-buttons" // fromYear={2000} // toYear={new Date().getFullYear()} - toDate={new Date()} // 금일까지만 표시됨 (금일 이후는 안보임) showOutsideDays={false} // 금월 까지만 표출되도록 mode="single" selected={field?.value} onSelect={handleSelect} - initialFocus + disabled={{ after: new Date() }} // 금일까지만 표시됨 (금일 이후는 안보임) 20250721 캘린더가 변경됨!! locale={ko} /> diff --git a/packages/common/business_components/src/ui/select/index-2.tsx b/packages/common/business_components/src/ui/select/index-2.tsx deleted file mode 100644 index 8ff1f4582..000000000 --- a/packages/common/business_components/src/ui/select/index-2.tsx +++ /dev/null @@ -1,45 +0,0 @@ -/** - * @remarks - * 이 버전은 SearchModal에서 사용하기 위해 `SelectContent`의 z-index를 `z-[101]`로 수정하였습니다. - */ -import { Label } from "@common/components"; -import React from "react"; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./lib/selectBox"; - -/** 차트 기본 스타일 */ -interface SelectBox1 { - text:string, - val:string|number -} - -const SelectBox2 = React.forwardRef< - HTMLDivElement, - React.HTMLAttributes & { - label:string, - selectClass:string|undefined, - className:string, - selectArray:SelectBox1[], - onSelectValue:(val:string|number)=>void, - value?: string|number - } ->(({ label, className, selectClass, selectArray, onSelectValue, value }, ref)=>{ - const placeHolder = selectArray?.[0]?.text ?? ""; - - return ( -
- {label && } - -
- ) -}) - -export { SelectBox2 }; diff --git a/packages/common/business_components/src/ui/select/index.tsx b/packages/common/business_components/src/ui/select/index.tsx index d09e3023a..82db6da45 100644 --- a/packages/common/business_components/src/ui/select/index.tsx +++ b/packages/common/business_components/src/ui/select/index.tsx @@ -27,6 +27,7 @@ const SelectBox = React.forwardRef< onSelectValue: (val: string | number) => void; value?: string | number; readOnly?: boolean; // readOnly 속성 추가 + selectContentClass?: string; } >( ( @@ -38,6 +39,7 @@ const SelectBox = React.forwardRef< onSelectValue, value, readOnly = false, + selectContentClass, }, ref, ) => { @@ -87,7 +89,7 @@ const SelectBox = React.forwardRef< > - + {visibleItems.map((selectArray) => ( {selectArray.text} diff --git a/packages/common/business_components/src/ui/tab/index-2.tsx b/packages/common/business_components/src/ui/tab/index-2.tsx deleted file mode 100644 index 71c103835..000000000 --- a/packages/common/business_components/src/ui/tab/index-2.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { Tabs, TabsList, TabsTrigger } from '@common/components/ui'; -import React from 'react'; - -interface TabItem { - value: string; - label: string; - onClick?: () => void; -} - -interface UITabProps extends React.HTMLAttributes { - defaultValue?: string; - tabList: TabItem[]; - children: React.ReactNode; -} - -const UITab2 = React.forwardRef< - HTMLDivElement, - UITabProps ->(({defaultValue, tabList, children}, ref)=>{ - return ( - - - {tabList.map((tab) => ( - - {tab.label} - - ))} - - {children} - - ) -}) -export { UITab2 }; diff --git a/packages/common/business_components/src/ui/table/index-2.tsx b/packages/common/business_components/src/ui/table/index-2.tsx deleted file mode 100644 index 9756d2dfa..000000000 --- a/packages/common/business_components/src/ui/table/index-2.tsx +++ /dev/null @@ -1,155 +0,0 @@ -import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@common/components"; -import { useEffect, useState } from "react"; -import UiPageNations from "./uiPageNations"; - -type UiTableProps = { - headName: React.ReactNode | undefined; - tableData: { [key: string]: string | number | boolean | object }[]; - headlist: UiHeadProps[]; - pageSize: number; - total: number; - children: React.ReactNode | undefined; - cellClick: ((index: number | undefined, headId: string) => void) | undefined; - onPageChange: (page: number) => void; - className?: string; - resetPageNo?: boolean; - cur?: number; - height?: string; // ✅ 추가된 prop -}; - -type UiHeadProps = { - id: string; - title: string; - align?: string; -}; - -export function UiTable2({ - tableData, - headlist, - pageSize, - total, - children, - headName, - cellClick, - onPageChange, - className = '', - resetPageNo = false, - cur, - height = '45vh', // ✅ 기본값 설정 -}: UiTableProps) { - const [idx, setIdx] = useState(-1); - const [curState, setCurState] = useState(cur ?? 1); - - useEffect(() => { - setCurState(cur ?? 1); - }, [cur]); - - useEffect(() => { - if (resetPageNo) { - setCurState(1); - onPageChange(1); - } - }, [onPageChange, resetPageNo]); - - const handlePageChange = (page: number) => { - setCurState(page); - onPageChange(page); - }; - - const handleCellClick = (index: number, headId: string) => { - if (cellClick) { - cellClick(index, headId); - setIdx(index); - } - }; - - const startIdx = (curState - 1) * pageSize; - - return ( - <> - {headName && ( -
-
-

{headName}

-
-
- )} - -
- {/* ✅ 외부에서 조절 가능한 높이 적용 */} -
- - - {children ? ( - <>{children} - ) : ( - - {headlist.map((head) => ( - - {head.title} - - ))} - - )} - - - - {tableData.length > 0 ? ( - tableData.map((el, index) => ( - - {headlist.map((head) => ( - handleCellClick(startIdx + index, head.id)} - > - {typeof el[head.id] === 'number' - ? String(el[head.id]).replace(/\B(?=(\d{3})+(?!\d))/g, ",") - : String(el[head.id] ?? '')} - - ))} - - )) - ) : ( - - - {/* ✅ 높이 보장 + 가운데 정렬용 div */} -
- 🔍 검색 결과가 없습니다. -
-
-
- )} -
-
-
- - {/* 페이지네이션 하단 고정 */} - {( -
- -
- )} -
- - ); -} diff --git a/packages/common/business_components/src/ui/table/index.tsx b/packages/common/business_components/src/ui/table/index.tsx index 85ffb85ea..de6fed944 100644 --- a/packages/common/business_components/src/ui/table/index.tsx +++ b/packages/common/business_components/src/ui/table/index.tsx @@ -1,11 +1,21 @@ -"use client" +"use client"; -import { Checkbox, Table, TableBody, TableCell, TableHeader, TableRow } from "@common/components"; +import { + Checkbox, + Table, + TableBody, + TableCell, + TableHeader, + TableRow, +} from "@common/components"; import { useEffect, useState, useRef } from "react"; import { HeadMakeColSpan } from "./HeadMakeColSpan"; import UiPageNations from "./uiPageNations"; import { Check } from "lucide-react"; - +export type ChkClass = { + chk: boolean; + readonly: boolean; +}; type UiTableProps = { headName: React.ReactNode | undefined; tableData: { [key: string]: any }[]; @@ -20,22 +30,35 @@ type UiTableProps = { boldArray?: number[]; resetSelection?: boolean; index?: number; -} +}; type UiHeadProps = { id: string; title: string; align?: string; visible?: boolean; -} +}; -export function UiTable({ tableData, headlist, pageSize, total, headName, children, cellClick, onPageChange, className = '', resetPageNo = false, boldArray = [], resetSelection = false, index = -1 }: UiTableProps) { +export function UiTable({ + tableData, + headlist, + pageSize, + total, + headName, + children, + cellClick, + onPageChange, + className = "", + resetPageNo = false, + boldArray = [], + resetSelection = false, + index = -1, +}: UiTableProps) { const [cur, setCur] = useState(1); const [idx, setIdx] = useState(index); const [headerHeight, setHeaderHeight] = useState(0); const headerRef = useRef(null); - useEffect(() => { if (resetPageNo) { setCur(1); @@ -51,14 +74,14 @@ export function UiTable({ tableData, headlist, pageSize, total, headName, childr useEffect(() => { if (headerRef.current) { - setHeaderHeight(headerRef.current.offsetHeight-5); + setHeaderHeight(headerRef.current.offsetHeight - 5); } }, [tableData, headlist]); const handlePageChange = (page: number) => { setCur(page); onPageChange(page); - } + }; const handleCellClick = (index: number, headId: string) => { if (cellClick) { @@ -67,81 +90,158 @@ export function UiTable({ tableData, headlist, pageSize, total, headName, childr } }; - return ( <> - {headName ?
-

{headName}

-
: <>} -
+ {headName ? ( +
+

{headName}

+
+ ) : ( + <> + )} +
{children ? children : } - - {tableData.length > 0 ? tableData?.map((el: Record, index) => ( - - {headlist - .filter((item) => item.visible != false) - .map((head) => ( - head.id && head.id.length > 1 && ( - boldArray.includes(index) - ? - handleCellClick(index, head.id)} - key={`cell-${head.id}-${index}`} - > - {typeof el[head.id] !== 'number' ? String(el[head.id] ?? '') : String(el[head.id]).includes('.') - ? String(el[head.id]).split('.').map((part, index) => - index === 0 ? part.replace(/\B(?=(\d{3})+(?!\d))/g, ",") : part - ).join('.') - : String(el[head.id] ?? '').replace(/\B(?=(\d{3})+(?!\d))/g, ",")} - - : - + {tableData.length > 0 ? ( + tableData?.map((el: Record, index) => ( + + {headlist + .filter((item) => item.visible != false) + .map( + (head) => + head.id && + head.id.length > 1 && + (boldArray.includes(index) ? ( + handleCellClick(index, head.id)} + key={`cell-${head.id}-${index}`} + > + {typeof el[head.id] !== "number" + ? String(el[head.id] ?? "") + : String(el[head.id]).includes(".") + ? String(el[head.id]) + .split(".") + .map((part, index) => + index === 0 + ? part.replace( + /\B(?=(\d{3})+(?!\d))/g, + ",", + ) + : part, + ) + .join(".") + : String(el[head.id] ?? "").replace( + /\B(?=(\d{3})+(?!\d))/g, + ",", + )} + + ) : ( + handleCellClick(index, head.id)} - key={`cell-${head.id}-${index}`} - > - {typeof el[head.id] === 'boolean' - ? { - el[head.id] = checked; - }} - > - - - : - typeof el[head.id] !== 'number' ? - String(el[head.id] ?? '') : - String(el[head.id]).includes('.') - ? String(el[head.id]).split('.').map((part, index) => - index === 0 ? part.replace(/\B(?=(\d{3})+(?!\d))/g, ",") : part - ).join('.') - : - String(el[head.id] ?? '').replace(/\B(?=(\d{3})+(?!\d))/g, ",")} - - ) - ))} - - )) : ( + onClick={() => handleCellClick(index, head.id)} + key={`cell-${head.id}-${index}`} + > + { + //체크박스 유형애서 readOnly가 필요한 경우를 위해서 ChkClass 를 사용해서 readOnly 처리함. + typeof el[head.id] === "object" && + el[head.id] !== null && + "chk" in (el[head.id] as object) && + "readonly" in (el[head.id] as object) ? ( + { + { + console.log(el[head.id]); + } + if ( + (el[head.id] as ChkClass).readonly == true + ) + return; + (el[head.id] as ChkClass).chk = + Boolean(checked); + }} + > + + + ) : typeof el[head.id] === "boolean" ? ( + { + el[head.id] = checked; + }} + > + + + ) : typeof el[head.id] !== "number" ? ( + String(el[head.id] ?? "") + ) : String(el[head.id]).includes(".") ? ( + String(el[head.id]) + .split(".") + .map((part, index) => + index === 0 + ? part.replace( + /\B(?=(\d{3})+(?!\d))/g, + ",", + ) + : part, + ) + .join(".") + ) : ( + String(el[head.id] ?? "").replace( + /\B(?=(\d{3})+(?!\d))/g, + ",", + ) + ) + } + + )), + )} + + )) + ) : ( - item.visible != false).length} className="h-24 text-center text-gray-500"> + item.visible != false).length + } + className="h-24 text-center text-gray-500" + > 조회된 결과가 없습니다. )}
- {total <= 0 ? <> : } + {total <= 0 ? ( + <> + ) : ( + + )}
); -} \ No newline at end of file +}