feat: 품목관리 동적 렌더링 시스템 구현
- DynamicItemForm 컴포넌트 구조 생성 - DynamicField: 필드 타입별 렌더링 - DynamicSection: 섹션 단위 렌더링 - DynamicFormRenderer: 페이지 전체 렌더링 - 필드 타입별 컴포넌트 (TextField, NumberField, DropdownField, CheckboxField, DateField, FileField, CustomField) - 커스텀 훅 (useDynamicFormState, useFormStructure, useConditionalFields) - DataTable 공통 컴포넌트 (테이블, 페이지네이션, 검색, 탭필터, 통계카드) - ItemFormWrapper: Feature Flag 기반 폼 선택 - 타입 정의 및 문서화 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
/**
|
||||
* CheckboxField Component
|
||||
*
|
||||
* 체크박스/스위치 필드 (checkbox, switch)
|
||||
*/
|
||||
|
||||
'use client';
|
||||
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { Switch } from '@/components/ui/switch';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import type { DynamicFieldProps } from '../types';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
export function CheckboxField({
|
||||
field,
|
||||
value,
|
||||
error,
|
||||
onChange,
|
||||
onBlur,
|
||||
disabled,
|
||||
}: DynamicFieldProps) {
|
||||
const isSwitch = field.field_type === 'switch';
|
||||
const checked = value === true || value === 'true' || value === 1;
|
||||
|
||||
const handleChange = (newChecked: boolean) => {
|
||||
onChange(newChecked);
|
||||
onBlur();
|
||||
};
|
||||
|
||||
if (isSwitch) {
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<Label
|
||||
htmlFor={field.field_key}
|
||||
className={cn(
|
||||
'text-sm font-medium',
|
||||
field.is_required && "after:content-['*'] after:ml-0.5 after:text-red-500"
|
||||
)}
|
||||
>
|
||||
{field.field_name}
|
||||
</Label>
|
||||
<Switch
|
||||
id={field.field_key}
|
||||
checked={checked}
|
||||
onCheckedChange={handleChange}
|
||||
disabled={disabled || field.is_readonly}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{field.help_text && !error && (
|
||||
<p className="text-xs text-muted-foreground">{field.help_text}</p>
|
||||
)}
|
||||
|
||||
{error && <p className="text-xs text-red-500">{error}</p>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id={field.field_key}
|
||||
checked={checked}
|
||||
onCheckedChange={handleChange}
|
||||
disabled={disabled || field.is_readonly}
|
||||
/>
|
||||
<Label
|
||||
htmlFor={field.field_key}
|
||||
className={cn(
|
||||
'text-sm font-medium cursor-pointer',
|
||||
field.is_required && "after:content-['*'] after:ml-0.5 after:text-red-500",
|
||||
(disabled || field.is_readonly) && 'cursor-not-allowed opacity-50'
|
||||
)}
|
||||
>
|
||||
{field.field_name}
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
{field.help_text && !error && (
|
||||
<p className="text-xs text-muted-foreground ml-6">{field.help_text}</p>
|
||||
)}
|
||||
|
||||
{error && <p className="text-xs text-red-500 ml-6">{error}</p>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default CheckboxField;
|
||||
Reference in New Issue
Block a user