Files
frontend/apps/sn/app/components/ui/EditableFormTable.tsx
T
swcho a6e3731289 feat:
- excelToJson.ts 에 날짜 변환 함수 추가 및 타입 수정
- UITable 레이아웃을 이용한 입력 가능 Table인 EditableFormTable.tsx 컴포넌트 구현
- RDII 분석 페이지 수정 및 JSON 데이터 생성, API 호출 기능 구현
2025-05-28 11:07:04 +09:00

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>
);
}