- excelToJson.ts 에 날짜 변환 함수 추가 및 타입 수정 - UITable 레이아웃을 이용한 입력 가능 Table인 EditableFormTable.tsx 컴포넌트 구현 - RDII 분석 페이지 수정 및 JSON 데이터 생성, API 호출 기능 구현
131 lines
6.1 KiB
TypeScript
131 lines
6.1 KiB
TypeScript
import { Input, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@common/components/ui";
|
|
import type { UseFormReturn, FieldArrayWithId, Path, FieldValues } from "react-hook-form";
|
|
import { Controller } from "react-hook-form";
|
|
|
|
// 편집 가능한 테이블 헤더 항목 인터페이스
|
|
export interface EditableTableHeadItem<TItem extends FieldValues> {
|
|
id: keyof TItem | 'response'; // 'response'는 직접적인 키가 아닐 수 있지만 특별히 처리될 수 있음
|
|
title: string;
|
|
editable?: boolean; // 편집 가능 여부
|
|
width?: string; // 컬럼 너비 추가
|
|
align?: 'left' | 'center' | 'right'; // 정렬
|
|
cellClassName?: string; // 셀 클래스 이름
|
|
inputClassName?: string; // 입력 필드 클래스 이름
|
|
inputProps?: React.ComponentProps<typeof Input>; // Input 컴포넌트에 전달할 props (예: type="number")
|
|
}
|
|
|
|
// EditableFormTable 컴포넌트 props 타입 정의
|
|
type EditableFormTableProps<
|
|
TFormValues extends FieldValues,
|
|
TFieldArrayName extends Path<TFormValues>,
|
|
TFieldArrayItem extends FieldValues = TFormValues[TFieldArrayName] extends (infer U)[] ? U extends FieldValues ? U : never : never
|
|
> = {
|
|
form: UseFormReturn<TFormValues>; // react-hook-form의 form 객체
|
|
name: TFieldArrayName; // 필드 배열 이름 (예: "unitFactors")
|
|
fields: FieldArrayWithId<TFieldArrayItem, any, "id">[]; // useFieldArray에서 반환된 필드 배열
|
|
headlist: EditableTableHeadItem<TFieldArrayItem>[]; // 테이블 헤더 설정
|
|
tableClassName?: string; // 테이블 클래스 이름
|
|
headerClassName?: string; // 헤더 클래스 이름
|
|
bodyClassName?: string; // 바디 클래스 이름
|
|
rowClassName?: string; // 행 클래스 이름
|
|
};
|
|
|
|
// EditableFormTable 컴포넌트
|
|
export function EditableFormTable<
|
|
TFormValues extends FieldValues,
|
|
TFieldArrayName extends Path<TFormValues>,
|
|
TFieldArrayItem extends FieldValues = TFormValues[TFieldArrayName] extends (infer U)[] ? U extends FieldValues ? U : never : never
|
|
>({
|
|
form,
|
|
name,
|
|
fields,
|
|
headlist,
|
|
tableClassName = "w-full",
|
|
headerClassName = "font-bold",
|
|
bodyClassName = 'text-center',
|
|
rowClassName = "",
|
|
}: EditableFormTableProps<TFormValues, TFieldArrayName, TFieldArrayItem>) {
|
|
return (
|
|
<div className={`card !m-[1.2rem] !p-0r`}>
|
|
<Table variant="secondary" className={tableClassName}>
|
|
<TableHeader className={headerClassName}>
|
|
<TableRow>
|
|
{headlist.map((head) => (
|
|
<TableHead
|
|
key={String(head.id)}
|
|
className={head.align ? `text-${head.align}` : 'text-center'}
|
|
style={{ width: head.width }} // head.width 스타일 적용
|
|
>
|
|
{head.title}
|
|
</TableHead>
|
|
))}
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody className={bodyClassName}>
|
|
{fields.map((rowItem, rowIndex) => (
|
|
<TableRow key={rowItem.id} className={rowClassName}>
|
|
{headlist.map((head) => {
|
|
const fieldName = `${name}.${rowIndex}.${String(head.id)}` as Path<TFormValues>;
|
|
const cellValue = (rowItem as any)[head.id as string];
|
|
|
|
return (
|
|
<TableCell key={`${rowItem.id}-${String(head.id)}`} className={`${head.align ? `text-${head.align}` : 'text-center'} ${head.cellClassName || ''}`}>
|
|
{head.editable ? (
|
|
<Controller
|
|
name={fieldName}
|
|
control={form.control}
|
|
render={({ field }) => {
|
|
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
const newValue = e.target.value;
|
|
|
|
if (newValue === "") {
|
|
field.onChange(""); // 빈 문자열 허용
|
|
return;
|
|
}
|
|
|
|
// 숫자와 '.'만 허용 (단일 '.' 허용)
|
|
const isValidChars = /^[0-9]*\.?[0-9]*$/.test(newValue);
|
|
if (!isValidChars) {
|
|
return; // 유효하지 않은 문자 또는 여러 개의 '.' 포함 시 변경 무시
|
|
}
|
|
|
|
// '.' 기호가 포함된 경우, 처음에는 올 수 없음 (예: '.5' 방지, '0.5' 유도)
|
|
// 이 조건은 사용자의 요구사항에 따라 조정될 수 있습니다.
|
|
// 현재 로직은 '.5'와 같은 입력을 허용하지 않습니다.
|
|
if (newValue.includes('.') && newValue.startsWith('.')) {
|
|
// 만약 '.'만 입력된 경우 허용하려면 아래 주석 해제
|
|
// if (newValue === '.') {
|
|
// field.onChange(newValue);
|
|
// return;
|
|
// }
|
|
return;
|
|
}
|
|
|
|
field.onChange(newValue);
|
|
};
|
|
|
|
return (
|
|
<Input
|
|
{...field} // field의 value, onBlur, name, ref 등을 전달
|
|
onChange={handleChange} // 커스텀 handleChange 사용
|
|
className={`h-8 w-full text-center ${head.inputClassName || ''}`}
|
|
{...head.inputProps} // 기존 inputProps (예: type="text")
|
|
// type="text"를 명시적으로 사용하거나 head.inputProps에서 설정하는 것이 좋습니다.
|
|
// type="number"와 함께 사용 시 브라우저 기본 동작과 충돌할 수 있습니다.
|
|
/>
|
|
);
|
|
}}
|
|
/>
|
|
) : (
|
|
<span>{String(cellValue ?? '')}</span>
|
|
)}
|
|
</TableCell>
|
|
);
|
|
})}
|
|
</TableRow>
|
|
))}
|
|
</TableBody>
|
|
</Table>
|
|
</div>
|
|
);
|
|
} |