Merge commit 'b1096ed9df49fd87b090f85376fab45ed2406bb7' into iipbs/wr/form_fuction

This commit is contained in:
jyha00
2025-07-07 20:32:46 +09:00
8 changed files with 128 additions and 102 deletions
@@ -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,9 +28,10 @@ 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();
@@ -46,7 +47,12 @@ const selectedYear = parseInt(yearFromProp);
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) =>
@@ -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>
@@ -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>
</>
);
}
@@ -199,7 +199,7 @@ function SearchFormLeft({ isShowPart = false, children, selectSidoData = [], sel
setRtnSidoText(selectedSido?.text || '');
}
initializeSigunData(val);
onSelectValue(selectSigunData[0]?.val || '0', 'sigun');
onSelectValue('0', 'sigun');
break;
case 'sigun':
if (setRtnSigunValue) {
@@ -1,3 +1,5 @@
// selectBox
import { Label } from "@common/components";
import React from "react";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./lib/selectBox";
@@ -5,7 +7,8 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from ".
/** 차트 기본 스타일 */
interface SelectBox1 {
text:string,
val:string|number
val:string|number,
hidden?: boolean
}
const SelectBox = React.forwardRef<
@@ -20,17 +23,29 @@ const SelectBox = React.forwardRef<
readOnly?: boolean // readOnly 속성 추가
}
>(({ label, className, selectClass, selectArray, onSelectValue, value, readOnly = false }, ref)=>{
const placeHolder = selectArray?.[0]?.text ?? "";
// hidden이 true인 첫 번째 항목을 placeholder로 사용
const hiddenItem = selectArray.find(item => item.hidden);
const placeHolder = hiddenItem?.text ?? "선택";
// hidden이 true인 항목만 드롭다운에서 제외
const visibleItems = selectArray.filter(item => !item.hidden);
// 현재 값이 hidden 항목이거나 빈 값인 경우 undefined로 설정하여 placeholder가 표시되도록 함
const currentValue = value !== undefined &&
hiddenItem &&
(String(value) === String(hiddenItem.val) || value === "" || value === "0" || value === 0)
? undefined
: (value !== undefined ? String(value) : undefined);
return (
<div className={className+" flex items-center"} ref={ref}>
{label && <Label className="whitespace-nowrap w-20 text-xl text-gray-600">{label}</Label>}
<Select onValueChange={onSelectValue} value={value !== undefined ? String(value) : undefined}>
<Select onValueChange={onSelectValue} value={currentValue} disabled={readOnly}>
<SelectTrigger className={selectClass?selectClass:"flex-1 p-2 border rounded"}>
<SelectValue placeholder={placeHolder} />
</SelectTrigger>
<SelectContent>
{selectArray.map((selectArray) => (
{visibleItems.map((selectArray) => (
<SelectItem key={selectArray.val} value={String(selectArray.val)}>{selectArray.text}</SelectItem>
))}
</SelectContent>
@@ -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' : ''}
@@ -141,7 +139,7 @@ export function UiTable2({
</div>
{/* 페이지네이션 하단 고정 */}
{ (
{(
<div className="sticky left-0 right-0 bottom-0 bg-white z-20 mt-5">
<UiPageNations
total={total}
@@ -9,28 +9,28 @@ import { boolean } from "zod";
type UiTableProps = {
headName: React.ReactNode | undefined;
tableData: { [key: string]:any}[];
tableData: { [key: string]: any }[];
headlist: UiHeadProps[];
pageSize: number;
total: number;
cellClick : ((index:number|undefined, headId:string)=>void )| undefined;
cellClick: ((index: number | undefined, headId: string) => void) | undefined;
onPageChange: (page: number) => void;
className?: string;
resetPageNo?: boolean;
children?: React.ReactNode | undefined;
boldArray?: number[];
resetSelection?: boolean;
index?:number;
index?: number;
}
type UiHeadProps = {
id: string;
title: string;
align?: string;
visible?:boolean;
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<number>(1);
const [idx, setIdx] = useState<number>(index);
const [isCheck, seIsCheck] = useState<boolean>(false);
@@ -54,77 +54,75 @@ export function UiTable({ tableData, headlist, pageSize, total, headName, childr
onPageChange(page);
}
const handleCellClick = (index: number,headId:string )=> {
const handleCellClick = (index: number, headId: string) => {
if (cellClick) {
cellClick(index,headId);
cellClick(index, headId);
setIdx(index);
}
};
return (
<>
{headName ? <div className="card-header">
<h3 className="text-2xl font-medium text-label">{headName}</h3>
</div> : <></>}
</div> : <></>}
<div className={`card !m-[1.2rem] !p-0r ${className}`}>
<Table variant="secondary">
<TableHeader className="font-bold">
{children ? children : <HeadMakeColSpan upHeadList={headlist} />}
</TableHeader>
<TableBody className='text-center'>
{tableData.length > 0 ? tableData?.map((el: { [key: string]: any}, index) => (
{tableData.length > 0 ? tableData?.map((el: { [key: string]: any }, index) => (
<TableRow key={`row-${index}`}>
{headlist
.filter((item) => item.visible != false)
.map((head) => (
head.id && head.id.length > 1 && (
boldArray.includes(index)
?
<TableCell
className={`${head.align === 'right' ? 'text-right' : head.align === 'center' ? 'text-center' : typeof el[head.id] === 'number' ? 'text-right' : 'text-center'} ${
cellClick && idx === index ? '!font-[1000] cursor-pointer' :
cellClick ? 'cursor-pointer' : ''
} !font-bold`}
onClick={() => 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, ",")}
</TableCell>
:
<TableCell
className={`${head.align === 'right' ? 'text-right' : head.align === 'center' ? 'text-center' : typeof el[head.id] === 'number' ? 'text-right' : 'text-center'} ${
cellClick && idx === index ? '!font-[1000] cursor-pointer' :
cellClick ? 'cursor-pointer' : ''
}`}
onClick={() => handleCellClick(index, head.id)}
key={`cell-${head.id}-${index}`}
>
{typeof el[head.id] === 'boolean'
?<Checkbox
checked={el[head.id] === true}
onCheckedChange={(checked) => {
el[head.id]= checked;
}}
>
<Check className="h-6 w-6 text-primary" />
</Checkbox>
:
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, ",")}
</TableCell>
)
))}
.filter((item) => item.visible != false)
.map((head) => (
head.id && head.id.length > 1 && (
boldArray.includes(index)
?
<TableCell
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`}
onClick={() => 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, ",")}
</TableCell>
:
<TableCell
className={`${head.align === 'right' ? 'text-right' : head.align === 'center' ? 'text-center' : 'text-center'} ${cellClick && idx === index ? '!font-[1000] cursor-pointer' :
cellClick ? 'cursor-pointer' : ''
}`}
onClick={() => handleCellClick(index, head.id)}
key={`cell-${head.id}-${index}`}
>
{typeof el[head.id] === 'boolean'
? <Checkbox
checked={el[head.id] === true}
onCheckedChange={(checked) => {
el[head.id] = checked;
}}
>
<Check className="h-6 w-6 text-primary" />
</Checkbox>
:
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, ",")}
</TableCell>
)
))}
</TableRow>
)) : <></>}
</TableBody>
@@ -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,9 @@ 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 +68,8 @@ export default function UiPageNations(commonPageNationsProps: CommonPageNationsP
<PaginationItem>
<PaginationNext
href="#"
onClick={() => {
onClick={(e) => {
e.preventDefault();
if (endPage < totalPages) handlePageChange(endPage + 1);
}}
/>