Files
sam-react-prod/src/components/items/DynamicItemForm/fields/DropdownField.tsx

119 lines
3.0 KiB
TypeScript
Raw Normal View History

/**
* DropdownField Component
*
* / (dropdown, searchable-dropdown)
*/
'use client';
import { useState, useEffect } from 'react';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import { Label } from '@/components/ui/label';
import type { DynamicFieldProps, DropdownOption } from '../types';
import { cn } from '@/lib/utils';
export function DropdownField({
field,
value,
error,
onChange,
onBlur,
disabled,
}: DynamicFieldProps) {
const [options, setOptions] = useState<DropdownOption[]>(
field.dropdown_config?.options || []
);
const [isLoading, setIsLoading] = useState(false);
// API에서 옵션 로드 (options_endpoint가 있는 경우)
useEffect(() => {
if (field.dropdown_config?.options_endpoint) {
setIsLoading(true);
fetch(field.dropdown_config.options_endpoint)
.then((res) => res.json())
.then((data) => {
if (data.success && Array.isArray(data.data)) {
setOptions(data.data);
}
})
.catch((err) => {
console.error('[DropdownField] Failed to load options:', err);
})
.finally(() => {
setIsLoading(false);
});
}
}, [field.dropdown_config?.options_endpoint]);
const displayValue = value === null || value === undefined ? '' : String(value);
const handleValueChange = (newValue: string) => {
onChange(newValue);
onBlur();
};
return (
<div className="space-y-2">
<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>
<Select
value={displayValue}
onValueChange={handleValueChange}
disabled={disabled || field.is_readonly || isLoading}
>
<SelectTrigger
id={field.field_key}
className={cn(
error && 'border-red-500 focus:ring-red-500',
field.is_readonly && 'bg-gray-50 text-gray-500'
)}
>
<SelectValue
placeholder={
isLoading
? '로딩 중...'
: field.dropdown_config?.placeholder || '선택하세요'
}
/>
</SelectTrigger>
<SelectContent>
{field.dropdown_config?.allow_empty && (
<SelectItem value=""> </SelectItem>
)}
{options.map((option) => (
<SelectItem
key={option.value}
value={option.value}
disabled={option.disabled}
>
{option.label}
</SelectItem>
))}
</SelectContent>
</Select>
{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>
);
}
export default DropdownField;