속성창 줄이기

This commit is contained in:
kjs 2025-10-28 16:16:00 +09:00
parent 775fbf8903
commit 711e051b1c
14 changed files with 233 additions and 362 deletions

View File

@ -29,59 +29,6 @@ export default function StyleEditor({ style, onStyleChange, className }: StyleEd
return ( return (
<div className={`space-y-6 p-6 ${className}`}> <div className={`space-y-6 p-6 ${className}`}>
{/* 여백 섹션 */}
<div className="space-y-3">
<div className="flex items-center gap-2">
<Box className="text-primary h-4 w-4" />
<h3 className="text-sm font-semibold"></h3>
</div>
<Separator className="my-2" />
<div className="space-y-3">
<div className="grid grid-cols-2 gap-3">
<div className="space-y-1.5">
<Label htmlFor="margin" className="text-xs font-medium">
</Label>
<Input
id="margin"
type="text"
placeholder="10px"
value={localStyle.margin || ""}
onChange={(e) => handleStyleChange("margin", e.target.value)}
className="h-8"
/>
</div>
<div className="space-y-1.5">
<Label htmlFor="padding" className="text-xs font-medium">
</Label>
<Input
id="padding"
type="text"
placeholder="10px"
value={localStyle.padding || ""}
onChange={(e) => handleStyleChange("padding", e.target.value)}
className="h-8"
/>
</div>
</div>
<div className="space-y-1.5">
<Label htmlFor="gap" className="text-xs font-medium">
</Label>
<Input
id="gap"
type="text"
placeholder="10px"
value={localStyle.gap || ""}
onChange={(e) => handleStyleChange("gap", e.target.value)}
className="h-8"
/>
</div>
</div>
</div>
{/* 테두리 섹션 */} {/* 테두리 섹션 */}
<div className="space-y-3"> <div className="space-y-3">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
@ -101,7 +48,7 @@ export default function StyleEditor({ style, onStyleChange, className }: StyleEd
placeholder="1px" placeholder="1px"
value={localStyle.borderWidth || ""} value={localStyle.borderWidth || ""}
onChange={(e) => handleStyleChange("borderWidth", e.target.value)} onChange={(e) => handleStyleChange("borderWidth", e.target.value)}
className="h-8" className="h-8 text-xs"
/> />
</div> </div>
<div className="space-y-1.5"> <div className="space-y-1.5">
@ -112,7 +59,7 @@ export default function StyleEditor({ style, onStyleChange, className }: StyleEd
value={localStyle.borderStyle || "solid"} value={localStyle.borderStyle || "solid"}
onValueChange={(value) => handleStyleChange("borderStyle", value)} onValueChange={(value) => handleStyleChange("borderStyle", value)}
> >
<SelectTrigger className="h-8"> <SelectTrigger className="h-8 text-xs">
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@ -136,14 +83,14 @@ export default function StyleEditor({ style, onStyleChange, className }: StyleEd
type="color" type="color"
value={localStyle.borderColor || "#000000"} value={localStyle.borderColor || "#000000"}
onChange={(e) => handleStyleChange("borderColor", e.target.value)} onChange={(e) => handleStyleChange("borderColor", e.target.value)}
className="h-8 w-14 p-1" className="h-8 w-14 p-1 text-xs"
/> />
<Input <Input
type="text" type="text"
value={localStyle.borderColor || "#000000"} value={localStyle.borderColor || "#000000"}
onChange={(e) => handleStyleChange("borderColor", e.target.value)} onChange={(e) => handleStyleChange("borderColor", e.target.value)}
placeholder="#000000" placeholder="#000000"
className="h-8 flex-1" className="h-8 flex-1 text-xs"
/> />
</div> </div>
</div> </div>
@ -157,7 +104,7 @@ export default function StyleEditor({ style, onStyleChange, className }: StyleEd
placeholder="5px" placeholder="5px"
value={localStyle.borderRadius || ""} value={localStyle.borderRadius || ""}
onChange={(e) => handleStyleChange("borderRadius", e.target.value)} onChange={(e) => handleStyleChange("borderRadius", e.target.value)}
className="h-8" className="h-8 text-xs"
/> />
</div> </div>
</div> </div>
@ -182,14 +129,14 @@ export default function StyleEditor({ style, onStyleChange, className }: StyleEd
type="color" type="color"
value={localStyle.backgroundColor || "#ffffff"} value={localStyle.backgroundColor || "#ffffff"}
onChange={(e) => handleStyleChange("backgroundColor", e.target.value)} onChange={(e) => handleStyleChange("backgroundColor", e.target.value)}
className="h-8 w-14 p-1" className="h-8 w-14 p-1 text-xs"
/> />
<Input <Input
type="text" type="text"
value={localStyle.backgroundColor || "#ffffff"} value={localStyle.backgroundColor || "#ffffff"}
onChange={(e) => handleStyleChange("backgroundColor", e.target.value)} onChange={(e) => handleStyleChange("backgroundColor", e.target.value)}
placeholder="#ffffff" placeholder="#ffffff"
className="h-8 flex-1" className="h-8 flex-1 text-xs"
/> />
</div> </div>
</div> </div>
@ -204,7 +151,7 @@ export default function StyleEditor({ style, onStyleChange, className }: StyleEd
placeholder="url('image.jpg')" placeholder="url('image.jpg')"
value={localStyle.backgroundImage || ""} value={localStyle.backgroundImage || ""}
onChange={(e) => handleStyleChange("backgroundImage", e.target.value)} onChange={(e) => handleStyleChange("backgroundImage", e.target.value)}
className="h-8" className="h-8 text-xs"
/> />
</div> </div>
</div> </div>
@ -229,14 +176,14 @@ export default function StyleEditor({ style, onStyleChange, className }: StyleEd
type="color" type="color"
value={localStyle.color || "#000000"} value={localStyle.color || "#000000"}
onChange={(e) => handleStyleChange("color", e.target.value)} onChange={(e) => handleStyleChange("color", e.target.value)}
className="h-8 w-14 p-1" className="h-8 w-14 p-1 text-xs"
/> />
<Input <Input
type="text" type="text"
value={localStyle.color || "#000000"} value={localStyle.color || "#000000"}
onChange={(e) => handleStyleChange("color", e.target.value)} onChange={(e) => handleStyleChange("color", e.target.value)}
placeholder="#000000" placeholder="#000000"
className="h-8 flex-1" className="h-8 flex-1 text-xs"
/> />
</div> </div>
</div> </div>
@ -250,7 +197,7 @@ export default function StyleEditor({ style, onStyleChange, className }: StyleEd
placeholder="14px" placeholder="14px"
value={localStyle.fontSize || ""} value={localStyle.fontSize || ""}
onChange={(e) => handleStyleChange("fontSize", e.target.value)} onChange={(e) => handleStyleChange("fontSize", e.target.value)}
className="h-8" className="h-8 text-xs"
/> />
</div> </div>
</div> </div>
@ -264,17 +211,31 @@ export default function StyleEditor({ style, onStyleChange, className }: StyleEd
value={localStyle.fontWeight || "normal"} value={localStyle.fontWeight || "normal"}
onValueChange={(value) => handleStyleChange("fontWeight", value)} onValueChange={(value) => handleStyleChange("fontWeight", value)}
> >
<SelectTrigger className="h-6 w-full text-[10px] px-2"> <SelectTrigger className="h-6 w-full px-2 text-[10px]">
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value="normal" className="text-[10px]"></SelectItem> <SelectItem value="normal" className="text-[10px]">
<SelectItem value="bold" className="text-[10px]"></SelectItem>
<SelectItem value="100" className="text-[10px]">100</SelectItem> </SelectItem>
<SelectItem value="400" className="text-[10px]">400</SelectItem> <SelectItem value="bold" className="text-[10px]">
<SelectItem value="500" className="text-[10px]">500</SelectItem>
<SelectItem value="600" className="text-[10px]">600</SelectItem> </SelectItem>
<SelectItem value="700" className="text-[10px]">700</SelectItem> <SelectItem value="100" className="text-[10px]">
100
</SelectItem>
<SelectItem value="400" className="text-[10px]">
400
</SelectItem>
<SelectItem value="500" className="text-[10px]">
500
</SelectItem>
<SelectItem value="600" className="text-[10px]">
600
</SelectItem>
<SelectItem value="700" className="text-[10px]">
700
</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>
@ -286,14 +247,22 @@ export default function StyleEditor({ style, onStyleChange, className }: StyleEd
value={localStyle.textAlign || "left"} value={localStyle.textAlign || "left"}
onValueChange={(value) => handleStyleChange("textAlign", value)} onValueChange={(value) => handleStyleChange("textAlign", value)}
> >
<SelectTrigger className="h-6 w-full text-[10px] px-2"> <SelectTrigger className="h-6 w-full px-2 text-[10px]">
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
<SelectItem value="left" className="text-[10px]"></SelectItem> <SelectItem value="left" className="text-[10px]">
<SelectItem value="center" className="text-[10px]"></SelectItem>
<SelectItem value="right" className="text-[10px]"></SelectItem> </SelectItem>
<SelectItem value="justify" className="text-[10px]"></SelectItem> <SelectItem value="center" className="text-[10px]">
</SelectItem>
<SelectItem value="right" className="text-[10px]">
</SelectItem>
<SelectItem value="justify" className="text-[10px]">
</SelectItem>
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>

View File

@ -515,94 +515,60 @@ export const ButtonConfigPanel: React.FC<ButtonConfigPanelProps> = ({
{/* 테이블 이력 보기 액션 설정 */} {/* 테이블 이력 보기 액션 설정 */}
{(component.componentConfig?.action?.type || "save") === "view_table_history" && ( {(component.componentConfig?.action?.type || "save") === "view_table_history" && (
<div className="mt-4 space-y-4"> <div className="mt-4 space-y-4">
<h4 className="text-sm font-medium">📜 </h4>
<div> <div>
<Label> <Label>
() <span className="text-red-600">*</span> () <span className="text-red-600">*</span>
</Label> </Label>
{!config.action?.historyTableName && !currentTableName ? ( <Popover open={displayColumnOpen} onOpenChange={setDisplayColumnOpen}>
<div className="mt-2 rounded-md border border-yellow-300 bg-yellow-50 p-3"> <PopoverTrigger asChild>
<p className="text-xs text-yellow-800"> <Button
<strong></strong> , . variant="outline"
</p> role="combobox"
</div> aria-expanded={displayColumnOpen}
) : ( className="mt-2 h-8 w-full justify-between text-xs"
<> disabled={columnsLoading || tableColumns.length === 0}
{!config.action?.historyTableName && currentTableName && ( >
<div className="mt-2 rounded-md border border-green-300 bg-green-50 p-2"> {columnsLoading
<p className="text-xs text-green-800"> ? "로딩 중..."
<strong>{currentTableName}</strong>() . : config.action?.historyDisplayColumn
</p> ? config.action.historyDisplayColumn
</div> : tableColumns.length === 0
)} ? "사용 가능한 컬럼이 없습니다"
: "컬럼을 선택하세요"}
<Popover open={displayColumnOpen} onOpenChange={setDisplayColumnOpen}> <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
<PopoverTrigger asChild> </Button>
<Button </PopoverTrigger>
variant="outline" <PopoverContent className="p-0" style={{ width: "var(--radix-popover-trigger-width)" }} align="start">
role="combobox" <Command>
aria-expanded={displayColumnOpen} <CommandInput placeholder="컬럼 검색..." className="text-sm" />
className="mt-2 h-10 w-full justify-between text-sm" <CommandList>
disabled={columnsLoading || tableColumns.length === 0} <CommandEmpty className="text-sm"> .</CommandEmpty>
> <CommandGroup>
{columnsLoading {tableColumns.map((column) => (
? "로딩 중..." <CommandItem
: config.action?.historyDisplayColumn key={column}
? config.action.historyDisplayColumn value={column}
: tableColumns.length === 0 onSelect={(currentValue) => {
? "사용 가능한 컬럼이 없습니다" onUpdateProperty("componentConfig.action.historyDisplayColumn", currentValue);
: "컬럼을 선택하세요"} setDisplayColumnOpen(false);
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" /> }}
</Button> className="text-sm"
</PopoverTrigger> >
<PopoverContent className="p-0" style={{ width: "var(--radix-popover-trigger-width)" }} align="start"> <Check
<Command> className={cn(
<CommandInput placeholder="컬럼 검색..." className="text-sm" /> "mr-2 h-4 w-4",
<CommandList> config.action?.historyDisplayColumn === column ? "opacity-100" : "opacity-0",
<CommandEmpty className="text-sm"> .</CommandEmpty> )}
<CommandGroup> />
{tableColumns.map((column) => ( {column}
<CommandItem </CommandItem>
key={column} ))}
value={column} </CommandGroup>
onSelect={(currentValue) => { </CommandList>
onUpdateProperty("componentConfig.action.historyDisplayColumn", currentValue); </Command>
setDisplayColumnOpen(false); </PopoverContent>
}} </Popover>
className="text-sm"
>
<Check
className={cn(
"mr-2 h-4 w-4",
config.action?.historyDisplayColumn === column ? "opacity-100" : "opacity-0",
)}
/>
{column}
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
<p className="mt-2 text-xs text-gray-700">
<strong> </strong> .
<br />
: <code className="rounded bg-white px-1">device_code</code> &quot;DTG-001&quot;
.
<br /> .
</p>
{tableColumns.length === 0 && !columnsLoading && (
<p className="mt-2 text-xs text-red-600">
ID .
</p>
)}
</>
)}
</div> </div>
</div> </div>
)} )}
@ -693,6 +659,7 @@ export const ButtonConfigPanel: React.FC<ButtonConfigPanelProps> = ({
setLocalInputs((prev) => ({ ...prev, targetUrl: newValue })); setLocalInputs((prev) => ({ ...prev, targetUrl: newValue }));
onUpdateProperty("componentConfig.action.targetUrl", newValue); onUpdateProperty("componentConfig.action.targetUrl", newValue);
}} }}
className="h-8 text-xs"
/> />
<p className="mt-1 text-xs text-gray-500">URL을 </p> <p className="mt-1 text-xs text-gray-500">URL을 </p>
</div> </div>

View File

@ -21,14 +21,14 @@ interface ComponentsPanelProps {
placedColumns?: Set<string>; // 이미 배치된 컬럼명 집합 placedColumns?: Set<string>; // 이미 배치된 컬럼명 집합
} }
export function ComponentsPanel({ export function ComponentsPanel({
className, className,
tables = [], tables = [],
searchTerm = "", searchTerm = "",
onSearchChange, onSearchChange,
onTableDragStart, onTableDragStart,
selectedTableName, selectedTableName,
placedColumns placedColumns,
}: ComponentsPanelProps) { }: ComponentsPanelProps) {
const [searchQuery, setSearchQuery] = useState(""); const [searchQuery, setSearchQuery] = useState("");
@ -176,8 +176,8 @@ export function ComponentsPanel({
</div> </div>
{/* 카테고리 탭 */} {/* 카테고리 탭 */}
<Tabs defaultValue="input" className="flex flex-1 flex-col"> <Tabs defaultValue="input" className="flex min-h-0 flex-1 flex-col">
<TabsList className="mb-3 grid h-8 w-full grid-cols-5"> <TabsList className="mb-3 grid h-8 w-full flex-shrink-0 grid-cols-5">
<TabsTrigger value="tables" className="flex items-center gap-1 px-1 text-xs"> <TabsTrigger value="tables" className="flex items-center gap-1 px-1 text-xs">
<Database className="h-3 w-3" /> <Database className="h-3 w-3" />
<span className="hidden sm:inline"></span> <span className="hidden sm:inline"></span>

View File

@ -1150,7 +1150,7 @@ export const DetailSettingsPanel: React.FC<DetailSettingsPanelProps> = ({
<div className="space-y-2"> <div className="space-y-2">
<label className="block text-sm font-medium text-gray-700"> </label> <label className="block text-sm font-medium text-gray-700"> </label>
<Select value={localDetailType} onValueChange={handleDetailTypeChange}> <Select value={localDetailType} onValueChange={handleDetailTypeChange}>
<SelectTrigger className="w-full bg-white"> <SelectTrigger className="h-8 w-full bg-white text-xs">
<SelectValue placeholder="세부 타입을 선택하세요" /> <SelectValue placeholder="세부 타입을 선택하세요" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>

View File

@ -551,11 +551,6 @@ const PropertiesPanelComponent: React.FC<PropertiesPanelProps> = ({
{/* 액션 버튼들 */} {/* 액션 버튼들 */}
<div className="flex flex-wrap gap-1.5"> <div className="flex flex-wrap gap-1.5">
<Button size="sm" variant="outline" onClick={onCopyComponent} className="h-8 px-2.5 text-xs">
<Copy className="mr-1 h-3 w-3" />
</Button>
{canGroup && ( {canGroup && (
<Button size="sm" variant="outline" onClick={onGroupComponents} className="h-8 px-2.5 text-xs"> <Button size="sm" variant="outline" onClick={onGroupComponents} className="h-8 px-2.5 text-xs">
<Group className="mr-1 h-3 w-3" /> <Group className="mr-1 h-3 w-3" />
@ -569,11 +564,6 @@ const PropertiesPanelComponent: React.FC<PropertiesPanelProps> = ({
</Button> </Button>
)} )}
<Button size="sm" variant="destructive" onClick={onDeleteComponent} className="h-8 px-2.5 text-xs">
<Trash2 className="mr-1 h-3 w-3" />
</Button>
</div> </div>
</div> </div>

View File

@ -82,9 +82,9 @@ const ResolutionPanel: React.FC<ResolutionPanelProps> = ({ currentResolution, on
<div className="space-y-4"> <div className="space-y-4">
{/* 프리셋 선택 */} {/* 프리셋 선택 */}
<div className="space-y-2"> <div className="space-y-2">
<Label className="text-sm font-medium"> </Label> <Label className="text-xs font-medium"> </Label>
<Select value={selectedPreset} onValueChange={handlePresetChange}> <Select value={selectedPreset} onValueChange={handlePresetChange}>
<SelectTrigger> <SelectTrigger className="h-8 text-xs">
<SelectValue placeholder="해상도를 선택하세요" /> <SelectValue placeholder="해상도를 선택하세요" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@ -93,7 +93,7 @@ const ResolutionPanel: React.FC<ResolutionPanelProps> = ({ currentResolution, on
{SCREEN_RESOLUTIONS.filter((r) => r.category === "desktop").map((resolution) => ( {SCREEN_RESOLUTIONS.filter((r) => r.category === "desktop").map((resolution) => (
<SelectItem key={resolution.name} value={resolution.name}> <SelectItem key={resolution.name} value={resolution.name}>
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<Monitor className="h-4 w-4 text-primary" /> <Monitor className="text-primary h-4 w-4" />
<span>{resolution.name}</span> <span>{resolution.name}</span>
</div> </div>
</SelectItem> </SelectItem>
@ -125,7 +125,7 @@ const ResolutionPanel: React.FC<ResolutionPanelProps> = ({ currentResolution, on
<div className="px-2 py-1 text-xs font-medium text-gray-500"> </div> <div className="px-2 py-1 text-xs font-medium text-gray-500"> </div>
<SelectItem value="custom"> <SelectItem value="custom">
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<Settings className="h-4 w-4 text-muted-foreground" /> <Settings className="text-muted-foreground h-4 w-4" />
<span> </span> <span> </span>
</div> </div>
</SelectItem> </SelectItem>
@ -139,43 +139,33 @@ const ResolutionPanel: React.FC<ResolutionPanelProps> = ({ currentResolution, on
<Label className="text-sm font-medium"> </Label> <Label className="text-sm font-medium"> </Label>
<div className="grid grid-cols-2 gap-3"> <div className="grid grid-cols-2 gap-3">
<div className="space-y-1"> <div className="space-y-1">
<Label className="text-xs text-muted-foreground"> (px)</Label> <Label className="text-muted-foreground text-xs"> (px)</Label>
<Input <Input
type="number" type="number"
value={customWidth} value={customWidth}
onChange={(e) => setCustomWidth(e.target.value)} onChange={(e) => setCustomWidth(e.target.value)}
placeholder="1920" placeholder="1920"
min="1" min="1"
className="h-8 text-xs"
/> />
</div> </div>
<div className="space-y-1"> <div className="space-y-1">
<Label className="text-xs text-muted-foreground"> (px)</Label> <Label className="text-muted-foreground text-xs"> (px)</Label>
<Input <Input
type="number" type="number"
value={customHeight} value={customHeight}
onChange={(e) => setCustomHeight(e.target.value)} onChange={(e) => setCustomHeight(e.target.value)}
placeholder="1080" placeholder="1080"
min="1" min="1"
className="h-8 text-xs"
/> />
</div> </div>
</div> </div>
<Button onClick={handleCustomResolution} size="sm" className="w-full"> <Button onClick={handleCustomResolution} size="sm" className="h-8 w-full text-xs">
</Button> </Button>
</div> </div>
)} )}
{/* 해상도 정보 */}
<div className="space-y-2 text-xs text-gray-500">
<div className="flex items-center justify-between">
<span> :</span>
<span>{(currentResolution.width / currentResolution.height).toFixed(2)}:1</span>
</div>
<div className="flex items-center justify-between">
<span> :</span>
<span>{(currentResolution.width * currentResolution.height).toLocaleString()}</span>
</div>
</div>
</div> </div>
); );
}; };

View File

@ -202,15 +202,6 @@ export const UnifiedPropertiesPanel: React.FC<UnifiedPropertiesPanelProps> = ({
return ( return (
<div className="space-y-1.5"> <div className="space-y-1.5">
{/* 컴포넌트 정보 - 간소화 */}
<div className="bg-muted flex items-center justify-between rounded px-2 py-1">
<div className="flex items-center gap-1">
<Info className="text-muted-foreground h-2.5 w-2.5" />
<span className="text-foreground text-[10px] font-medium">{selectedComponent.type}</span>
</div>
<span className="text-muted-foreground text-[9px]">{selectedComponent.id.slice(0, 8)}</span>
</div>
{/* 라벨 + 최소 높이 (같은 행) */} {/* 라벨 + 최소 높이 (같은 행) */}
<div className="grid grid-cols-2 gap-1.5"> <div className="grid grid-cols-2 gap-1.5">
<div className="space-y-0.5"> <div className="space-y-0.5">
@ -300,24 +291,15 @@ export const UnifiedPropertiesPanel: React.FC<UnifiedPropertiesPanelProps> = ({
</div> </div>
)} )}
{/* 위치 */} {/* Z-Index */}
<div className="grid grid-cols-3 gap-2"> <div className="space-y-0.5">
<div> <Label className="text-[10px]">Z-Index ()</Label>
<Label>X {dragState?.isDragging && <Badge variant="secondary"></Badge>}</Label> <Input
<Input type="number" value={Math.round(currentPosition.x || 0)} disabled /> type="number"
</div> value={currentPosition.z || 1}
<div> onChange={(e) => handleUpdate("position.z", parseInt(e.target.value) || 1)}
<Label>Y</Label> className="h-6 text-[10px]"
<Input type="number" value={Math.round(currentPosition.y || 0)} disabled /> />
</div>
<div>
<Label>Z</Label>
<Input
type="number"
value={currentPosition.z || 1}
onChange={(e) => handleUpdate("position.z", parseInt(e.target.value) || 1)}
/>
</div>
</div> </div>
{/* 라벨 스타일 */} {/* 라벨 스타일 */}
@ -389,33 +371,6 @@ export const UnifiedPropertiesPanel: React.FC<UnifiedPropertiesPanelProps> = ({
</div> </div>
)} )}
</div> </div>
{/* 액션 버튼 */}
<Separator />
<div className="flex gap-2">
{onCopyComponent && (
<Button
variant="outline"
size="sm"
onClick={() => onCopyComponent(selectedComponent.id)}
className="flex-1"
>
<Copy className="mr-2 h-4 w-4" />
</Button>
)}
{onDeleteComponent && (
<Button
variant="outline"
size="sm"
onClick={() => onDeleteComponent(selectedComponent.id)}
className="flex-1 text-red-600 hover:bg-red-50 hover:text-red-700"
>
<Trash2 className="mr-2 h-4 w-4" />
</Button>
)}
</div>
</div> </div>
); );
}; };
@ -513,7 +468,7 @@ export const UnifiedPropertiesPanel: React.FC<UnifiedPropertiesPanelProps> = ({
<div> <div>
<Label> </Label> <Label> </Label>
<Select value={localComponentDetailType || webType} onValueChange={handleDetailTypeChange}> <Select value={localComponentDetailType || webType} onValueChange={handleDetailTypeChange}>
<SelectTrigger> <SelectTrigger className="h-8 text-xs">
<SelectValue placeholder="세부 타입 선택" /> <SelectValue placeholder="세부 타입 선택" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@ -561,7 +516,7 @@ export const UnifiedPropertiesPanel: React.FC<UnifiedPropertiesPanelProps> = ({
<div> <div>
<Label> </Label> <Label> </Label>
<Select value={widget.webType} onValueChange={(value) => handleUpdate("webType", value)}> <Select value={widget.webType} onValueChange={(value) => handleUpdate("webType", value)}>
<SelectTrigger> <SelectTrigger className="h-8 text-xs">
<SelectValue /> <SelectValue />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>

View File

@ -90,11 +90,11 @@ export const CheckboxTypeConfigPanel: React.FC<CheckboxTypeConfigPanelProps> = (
const newConfig = JSON.parse(JSON.stringify(currentValues)); const newConfig = JSON.parse(JSON.stringify(currentValues));
// console.log("☑️ CheckboxTypeConfig 업데이트:", { // console.log("☑️ CheckboxTypeConfig 업데이트:", {
// key, // key,
// value, // value,
// oldConfig: safeConfig, // oldConfig: safeConfig,
// newConfig, // newConfig,
// localValues, // localValues,
// }); // });
setTimeout(() => { setTimeout(() => {
@ -122,7 +122,7 @@ export const CheckboxTypeConfigPanel: React.FC<CheckboxTypeConfigPanelProps> = (
</Label> </Label>
<Select value={localValues.labelPosition} onValueChange={(value) => updateConfig("labelPosition", value)}> <Select value={localValues.labelPosition} onValueChange={(value) => updateConfig("labelPosition", value)}>
<SelectTrigger className="mt-1"> <SelectTrigger className="mt-1 h-8 text-xs">
<SelectValue placeholder="라벨 위치 선택" /> <SelectValue placeholder="라벨 위치 선택" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@ -218,7 +218,7 @@ export const CheckboxTypeConfigPanel: React.FC<CheckboxTypeConfigPanelProps> = (
{/* 안내 메시지 */} {/* 안내 메시지 */}
{localValues.indeterminate && ( {localValues.indeterminate && (
<div className="rounded-md bg-accent p-3"> <div className="bg-accent rounded-md p-3">
<div className="text-sm font-medium text-blue-900"> </div> <div className="text-sm font-medium text-blue-900"> </div>
<div className="mt-1 text-xs text-blue-800"> <div className="mt-1 text-xs text-blue-800">
. .

View File

@ -105,10 +105,10 @@ export const CodeTypeConfigPanel: React.FC<CodeTypeConfigPanelProps> = ({ config
// 실제 config 업데이트 // 실제 config 업데이트
const newConfig = { ...safeConfig, [key]: value }; const newConfig = { ...safeConfig, [key]: value };
// console.log("💻 CodeTypeConfig 업데이트:", { // console.log("💻 CodeTypeConfig 업데이트:", {
// key, // key,
// value, // value,
// oldConfig: safeConfig, // oldConfig: safeConfig,
// newConfig, // newConfig,
// }); // });
onConfigChange(newConfig); onConfigChange(newConfig);
}; };
@ -121,7 +121,7 @@ export const CodeTypeConfigPanel: React.FC<CodeTypeConfigPanelProps> = ({ config
</Label> </Label>
<Select value={localValues.language} onValueChange={(value) => updateConfig("language", value)}> <Select value={localValues.language} onValueChange={(value) => updateConfig("language", value)}>
<SelectTrigger className="mt-1"> <SelectTrigger className="mt-1 h-8 text-xs">
<SelectValue placeholder="언어 선택" /> <SelectValue placeholder="언어 선택" />
</SelectTrigger> </SelectTrigger>
<SelectContent className="max-h-60"> <SelectContent className="max-h-60">
@ -140,7 +140,7 @@ export const CodeTypeConfigPanel: React.FC<CodeTypeConfigPanelProps> = ({ config
</Label> </Label>
<Select value={localValues.theme} onValueChange={(value) => updateConfig("theme", value)}> <Select value={localValues.theme} onValueChange={(value) => updateConfig("theme", value)}>
<SelectTrigger className="mt-1"> <SelectTrigger className="mt-1 h-8 text-xs">
<SelectValue placeholder="테마 선택" /> <SelectValue placeholder="테마 선택" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@ -271,7 +271,7 @@ export const CodeTypeConfigPanel: React.FC<CodeTypeConfigPanelProps> = ({ config
</div> </div>
{/* 안내 메시지 */} {/* 안내 메시지 */}
<div className="rounded-md bg-accent p-3"> <div className="bg-accent rounded-md p-3">
<div className="text-sm font-medium text-blue-900"> </div> <div className="text-sm font-medium text-blue-900"> </div>
<div className="mt-1 text-xs text-blue-800"> <div className="mt-1 text-xs text-blue-800">

View File

@ -27,8 +27,8 @@ export const DateTypeConfigPanel: React.FC<DateTypeConfigPanelProps> = ({ config
// 로컬 상태로 실시간 입력 관리 // 로컬 상태로 실시간 입력 관리
const [localValues, setLocalValues] = useState(() => { const [localValues, setLocalValues] = useState(() => {
// console.log("📅 DateTypeConfigPanel 초기 상태 설정:", { // console.log("📅 DateTypeConfigPanel 초기 상태 설정:", {
// config, // config,
// safeConfig, // safeConfig,
// }); // });
return { return {
@ -47,17 +47,17 @@ export const DateTypeConfigPanel: React.FC<DateTypeConfigPanelProps> = ({ config
const hasValidConfig = config && Object.keys(config).length > 0; const hasValidConfig = config && Object.keys(config).length > 0;
// console.log("📅 DateTypeConfigPanel config 변경 감지:", { // console.log("📅 DateTypeConfigPanel config 변경 감지:", {
// config, // config,
// configExists: !!config, // configExists: !!config,
// configKeys: config ? Object.keys(config) : [], // configKeys: config ? Object.keys(config) : [],
// hasValidConfig, // hasValidConfig,
// safeConfig, // safeConfig,
// safeConfigKeys: Object.keys(safeConfig), // safeConfigKeys: Object.keys(safeConfig),
// currentLocalValues: localValues, // currentLocalValues: localValues,
// configStringified: JSON.stringify(config), // configStringified: JSON.stringify(config),
// safeConfigStringified: JSON.stringify(safeConfig), // safeConfigStringified: JSON.stringify(safeConfig),
// willUpdateLocalValues: hasValidConfig, // willUpdateLocalValues: hasValidConfig,
// timestamp: new Date().toISOString(), // timestamp: new Date().toISOString(),
// }); // });
// config가 없거나 비어있으면 로컬 상태를 유지 // config가 없거나 비어있으면 로컬 상태를 유지
@ -85,17 +85,17 @@ export const DateTypeConfigPanel: React.FC<DateTypeConfigPanelProps> = ({ config
localValues.maxDate !== newLocalValues.maxDate; localValues.maxDate !== newLocalValues.maxDate;
// console.log("🔄 로컬 상태 업데이트 검사:", { // console.log("🔄 로컬 상태 업데이트 검사:", {
// oldLocalValues: localValues, // oldLocalValues: localValues,
// newLocalValues, // newLocalValues,
// hasChanges, // hasChanges,
// changes: { // changes: {
// format: localValues.format !== newLocalValues.format, // format: localValues.format !== newLocalValues.format,
// showTime: localValues.showTime !== newLocalValues.showTime, // showTime: localValues.showTime !== newLocalValues.showTime,
// defaultValue: localValues.defaultValue !== newLocalValues.defaultValue, // defaultValue: localValues.defaultValue !== newLocalValues.defaultValue,
// placeholder: localValues.placeholder !== newLocalValues.placeholder, // placeholder: localValues.placeholder !== newLocalValues.placeholder,
// minDate: localValues.minDate !== newLocalValues.minDate, // minDate: localValues.minDate !== newLocalValues.minDate,
// maxDate: localValues.maxDate !== newLocalValues.maxDate, // maxDate: localValues.maxDate !== newLocalValues.maxDate,
// }, // },
// }); // });
if (hasChanges) { if (hasChanges) {
@ -113,34 +113,34 @@ export const DateTypeConfigPanel: React.FC<DateTypeConfigPanelProps> = ({ config
// 실제 config 업데이트 - 현재 로컬 상태를 기반으로 새 객체 생성 (safeConfig 기본값 덮어쓰기 방지) // 실제 config 업데이트 - 현재 로컬 상태를 기반으로 새 객체 생성 (safeConfig 기본값 덮어쓰기 방지)
const newConfig = JSON.parse(JSON.stringify({ ...localValues, [key]: value })); const newConfig = JSON.parse(JSON.stringify({ ...localValues, [key]: value }));
// console.log("📅 DateTypeConfig 업데이트:", { // console.log("📅 DateTypeConfig 업데이트:", {
// key, // key,
// value, // value,
// oldConfig: safeConfig, // oldConfig: safeConfig,
// newConfig, // newConfig,
// localValues, // localValues,
// timestamp: new Date().toISOString(), // timestamp: new Date().toISOString(),
// changes: { // changes: {
// format: newConfig.format !== safeConfig.format, // format: newConfig.format !== safeConfig.format,
// showTime: newConfig.showTime !== safeConfig.showTime, // showTime: newConfig.showTime !== safeConfig.showTime,
// placeholder: newConfig.placeholder !== safeConfig.placeholder, // placeholder: newConfig.placeholder !== safeConfig.placeholder,
// minDate: newConfig.minDate !== safeConfig.minDate, // minDate: newConfig.minDate !== safeConfig.minDate,
// maxDate: newConfig.maxDate !== safeConfig.maxDate, // maxDate: newConfig.maxDate !== safeConfig.maxDate,
// defaultValue: newConfig.defaultValue !== safeConfig.defaultValue, // defaultValue: newConfig.defaultValue !== safeConfig.defaultValue,
// }, // },
// willCallOnConfigChange: true, // willCallOnConfigChange: true,
// }); // });
// console.log("🔄 onConfigChange 호출 직전:", { // console.log("🔄 onConfigChange 호출 직전:", {
// newConfig, // newConfig,
// configStringified: JSON.stringify(newConfig), // configStringified: JSON.stringify(newConfig),
// }); // });
// 약간의 지연을 두고 업데이트 (배치 업데이트 방지) // 약간의 지연을 두고 업데이트 (배치 업데이트 방지)
setTimeout(() => { setTimeout(() => {
// console.log("✅ onConfigChange 호출 완료:", { // console.log("✅ onConfigChange 호출 완료:", {
// key, // key,
// newConfig, // newConfig,
// timestamp: new Date().toISOString(), // timestamp: new Date().toISOString(),
// }); // });
onConfigChange(newConfig); onConfigChange(newConfig);
}, 0); }, 0);
@ -157,9 +157,9 @@ export const DateTypeConfigPanel: React.FC<DateTypeConfigPanelProps> = ({ config
value={localValues.format} value={localValues.format}
onValueChange={(value) => { onValueChange={(value) => {
// console.log("📅 날짜 형식 변경:", { // console.log("📅 날짜 형식 변경:", {
// oldFormat: localValues.format, // oldFormat: localValues.format,
// newFormat: value, // newFormat: value,
// oldShowTime: localValues.showTime, // oldShowTime: localValues.showTime,
// }); // });
// format 변경 시 showTime도 자동 동기화 // format 변경 시 showTime도 자동 동기화
@ -175,9 +175,9 @@ export const DateTypeConfigPanel: React.FC<DateTypeConfigPanelProps> = ({ config
); );
// console.log("🔄 format+showTime 동시 업데이트:", { // console.log("🔄 format+showTime 동시 업데이트:", {
// newFormat: value, // newFormat: value,
// newShowTime: hasTime, // newShowTime: hasTime,
// newConfig, // newConfig,
// }); // });
// 로컬 상태도 동시 업데이트 // 로컬 상태도 동시 업데이트
@ -193,7 +193,7 @@ export const DateTypeConfigPanel: React.FC<DateTypeConfigPanelProps> = ({ config
}, 0); }, 0);
}} }}
> >
<SelectTrigger className="mt-1"> <SelectTrigger className="mt-1 h-8 text-xs">
<SelectValue placeholder="날짜 형식 선택" /> <SelectValue placeholder="날짜 형식 선택" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@ -215,9 +215,9 @@ export const DateTypeConfigPanel: React.FC<DateTypeConfigPanelProps> = ({ config
onCheckedChange={(checked) => { onCheckedChange={(checked) => {
const newShowTime = !!checked; const newShowTime = !!checked;
// console.log("⏰ 시간 표시 체크박스 변경:", { // console.log("⏰ 시간 표시 체크박스 변경:", {
// oldShowTime: localValues.showTime, // oldShowTime: localValues.showTime,
// newShowTime, // newShowTime,
// currentFormat: localValues.format, // currentFormat: localValues.format,
// }); // });
// showTime 변경 시 format도 적절히 조정 // showTime 변경 시 format도 적절히 조정
@ -231,9 +231,9 @@ export const DateTypeConfigPanel: React.FC<DateTypeConfigPanelProps> = ({ config
} }
// console.log("🔄 showTime+format 동시 업데이트:", { // console.log("🔄 showTime+format 동시 업데이트:", {
// newShowTime, // newShowTime,
// oldFormat: localValues.format, // oldFormat: localValues.format,
// newFormat, // newFormat,
// }); // });
// 한 번에 두 값을 모두 업데이트 - 현재 로컬 상태 기반으로 생성 // 한 번에 두 값을 모두 업데이트 - 현재 로컬 상태 기반으로 생성

View File

@ -92,10 +92,10 @@ export const EntityTypeConfigPanel: React.FC<EntityTypeConfigPanelProps> = ({ co
// 실제 config 업데이트 // 실제 config 업데이트
const newConfig = { ...safeConfig, [key]: value }; const newConfig = { ...safeConfig, [key]: value };
// console.log("🏢 EntityTypeConfig 업데이트:", { // console.log("🏢 EntityTypeConfig 업데이트:", {
// key, // key,
// value, // value,
// oldConfig: safeConfig, // oldConfig: safeConfig,
// newConfig, // newConfig,
// }); // });
onConfigChange(newConfig); onConfigChange(newConfig);
}; };
@ -233,7 +233,7 @@ export const EntityTypeConfigPanel: React.FC<EntityTypeConfigPanelProps> = ({ co
</Label> </Label>
<Select value={localValues.displayFormat} onValueChange={(value) => updateConfig("displayFormat", value)}> <Select value={localValues.displayFormat} onValueChange={(value) => updateConfig("displayFormat", value)}>
<SelectTrigger className="mt-1"> <SelectTrigger className="mt-1 h-8 text-xs">
<SelectValue placeholder="형식 선택" /> <SelectValue placeholder="형식 선택" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@ -317,7 +317,7 @@ export const EntityTypeConfigPanel: React.FC<EntityTypeConfigPanelProps> = ({ co
<div className="mt-2"> <div className="mt-2">
<div className="flex items-center space-x-2 rounded border bg-white p-2"> <div className="flex items-center space-x-2 rounded border bg-white p-2">
<Search className="h-4 w-4 text-gray-400" /> <Search className="h-4 w-4 text-gray-400" />
<div className="flex-1 text-sm text-muted-foreground"> <div className="text-muted-foreground flex-1 text-sm">
{localValues.placeholder || `${localValues.referenceTable || "엔터티"}를 선택하세요`} {localValues.placeholder || `${localValues.referenceTable || "엔터티"}를 선택하세요`}
</div> </div>
<Database className="h-4 w-4 text-gray-400" /> <Database className="h-4 w-4 text-gray-400" />
@ -334,7 +334,7 @@ export const EntityTypeConfigPanel: React.FC<EntityTypeConfigPanelProps> = ({ co
</div> </div>
{/* 안내 메시지 */} {/* 안내 메시지 */}
<div className="rounded-md bg-accent p-3"> <div className="bg-accent rounded-md p-3">
<div className="text-sm font-medium text-blue-900"> </div> <div className="text-sm font-medium text-blue-900"> </div>
<div className="mt-1 text-xs text-blue-800"> <div className="mt-1 text-xs text-blue-800">
<strong> </strong>: <strong> </strong>:

View File

@ -89,12 +89,12 @@ export const NumberTypeConfigPanel: React.FC<NumberTypeConfigPanelProps> = ({ co
const newConfig = JSON.parse(JSON.stringify(currentValues)); const newConfig = JSON.parse(JSON.stringify(currentValues));
// console.log("🔢 NumberTypeConfig 업데이트:", { // console.log("🔢 NumberTypeConfig 업데이트:", {
// key, // key,
// value, // value,
// oldConfig: safeConfig, // oldConfig: safeConfig,
// newConfig, // newConfig,
// localValues, // localValues,
// timestamp: new Date().toISOString(), // timestamp: new Date().toISOString(),
// }); // });
// 약간의 지연을 두고 업데이트 (배치 업데이트 방지) // 약간의 지연을 두고 업데이트 (배치 업데이트 방지)
@ -111,7 +111,7 @@ export const NumberTypeConfigPanel: React.FC<NumberTypeConfigPanelProps> = ({ co
</Label> </Label>
<Select value={localValues.format} onValueChange={(value) => updateConfig("format", value)}> <Select value={localValues.format} onValueChange={(value) => updateConfig("format", value)}>
<SelectTrigger className="mt-1"> <SelectTrigger className="mt-1 h-8 text-xs">
<SelectValue placeholder="숫자 형식 선택" /> <SelectValue placeholder="숫자 형식 선택" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>

View File

@ -82,11 +82,11 @@ export const SelectTypeConfigPanel: React.FC<SelectTypeConfigPanelProps> = ({ co
// 실제 config 업데이트 - 깊은 복사로 새 객체 보장 // 실제 config 업데이트 - 깊은 복사로 새 객체 보장
const newConfig = JSON.parse(JSON.stringify({ ...safeConfig, [key]: value })); const newConfig = JSON.parse(JSON.stringify({ ...safeConfig, [key]: value }));
// console.log("📋 SelectTypeConfig 업데이트:", { // console.log("📋 SelectTypeConfig 업데이트:", {
// key, // key,
// value, // value,
// oldConfig: safeConfig, // oldConfig: safeConfig,
// newConfig, // newConfig,
// timestamp: new Date().toISOString(), // timestamp: new Date().toISOString(),
// }); // });
// 약간의 지연을 두고 업데이트 (배치 업데이트 방지) // 약간의 지연을 두고 업데이트 (배치 업데이트 방지)
@ -101,10 +101,10 @@ export const SelectTypeConfigPanel: React.FC<SelectTypeConfigPanelProps> = ({ co
const updatedOptions = [...(safeConfig.options || []), newOptionData]; const updatedOptions = [...(safeConfig.options || []), newOptionData];
// console.log(" SelectType 옵션 추가:", { // console.log(" SelectType 옵션 추가:", {
// newOption: newOptionData, // newOption: newOptionData,
// updatedOptions, // updatedOptions,
// currentLocalOptions: localOptions, // currentLocalOptions: localOptions,
// timestamp: new Date().toISOString(), // timestamp: new Date().toISOString(),
// }); // });
// 로컬 상태 즉시 업데이트 // 로컬 상태 즉시 업데이트
@ -128,9 +128,9 @@ export const SelectTypeConfigPanel: React.FC<SelectTypeConfigPanelProps> = ({ co
const removeOption = (index: number) => { const removeOption = (index: number) => {
// console.log(" SelectType 옵션 삭제:", { // console.log(" SelectType 옵션 삭제:", {
// removeIndex: index, // removeIndex: index,
// currentOptions: safeConfig.options, // currentOptions: safeConfig.options,
// currentLocalOptions: localOptions, // currentLocalOptions: localOptions,
// }); // });
// 로컬 상태 즉시 업데이트 // 로컬 상태 즉시 업데이트
@ -170,7 +170,7 @@ export const SelectTypeConfigPanel: React.FC<SelectTypeConfigPanelProps> = ({ co
value={localValues.placeholder} value={localValues.placeholder}
onChange={(e) => updateConfig("placeholder", e.target.value)} onChange={(e) => updateConfig("placeholder", e.target.value)}
placeholder="옵션을 선택하세요" placeholder="옵션을 선택하세요"
className="mt-1" className="mt-1 h-8 text-xs"
/> />
</div> </div>

View File

@ -94,11 +94,11 @@ export const TextTypeConfigPanel: React.FC<TextTypeConfigPanelProps> = ({ config
const newConfig = JSON.parse(JSON.stringify(currentValues)); const newConfig = JSON.parse(JSON.stringify(currentValues));
// console.log("📝 TextTypeConfig 업데이트:", { // console.log("📝 TextTypeConfig 업데이트:", {
// key, // key,
// value, // value,
// oldConfig: safeConfig, // oldConfig: safeConfig,
// newConfig, // newConfig,
// localValues, // localValues,
// }); // });
setTimeout(() => { setTimeout(() => {
@ -114,7 +114,7 @@ export const TextTypeConfigPanel: React.FC<TextTypeConfigPanelProps> = ({ config
</Label> </Label>
<Select value={localValues.format} onValueChange={(value) => updateConfig("format", value)}> <Select value={localValues.format} onValueChange={(value) => updateConfig("format", value)}>
<SelectTrigger className="mt-1"> <SelectTrigger className="mt-1 h-8 text-xs">
<SelectValue placeholder="입력 형식 선택" /> <SelectValue placeholder="입력 형식 선택" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@ -220,13 +220,13 @@ export const TextTypeConfigPanel: React.FC<TextTypeConfigPanelProps> = ({ config
</div> </div>
{localValues.autoInput && ( {localValues.autoInput && (
<div className="space-y-3 border-l-2 border-primary/20 pl-4"> <div className="border-primary/20 space-y-3 border-l-2 pl-4">
<div> <div>
<Label htmlFor="autoValueType" className="text-sm font-medium"> <Label htmlFor="autoValueType" className="text-sm font-medium">
</Label> </Label>
<Select value={localValues.autoValueType} onValueChange={(value) => updateConfig("autoValueType", value)}> <Select value={localValues.autoValueType} onValueChange={(value) => updateConfig("autoValueType", value)}>
<SelectTrigger className="mt-1"> <SelectTrigger className="mt-1 h-8 text-xs">
<SelectValue placeholder="자동값 타입 선택" /> <SelectValue placeholder="자동값 타입 선택" />
</SelectTrigger> </SelectTrigger>
<SelectContent> <SelectContent>
@ -256,7 +256,7 @@ export const TextTypeConfigPanel: React.FC<TextTypeConfigPanelProps> = ({ config
</div> </div>
)} )}
<div className="rounded-md bg-accent p-3"> <div className="bg-accent rounded-md p-3">
<div className="text-sm font-medium text-blue-900"> </div> <div className="text-sm font-medium text-blue-900"> </div>
<div className="mt-1 text-xs text-blue-800"> <div className="mt-1 text-xs text-blue-800">
, . , .
@ -280,7 +280,7 @@ export const TextTypeConfigPanel: React.FC<TextTypeConfigPanelProps> = ({ config
{/* 형식별 안내 메시지 */} {/* 형식별 안내 메시지 */}
{localValues.format !== "none" && ( {localValues.format !== "none" && (
<div className="rounded-md bg-accent p-3"> <div className="bg-accent rounded-md p-3">
<div className="text-sm font-medium text-blue-900"> </div> <div className="text-sm font-medium text-blue-900"> </div>
<div className="mt-1 text-xs text-blue-800"> <div className="mt-1 text-xs text-blue-800">
{localValues.format === "email" && "유효한 이메일 주소를 입력해야 합니다 (예: user@example.com)"} {localValues.format === "email" && "유효한 이메일 주소를 입력해야 합니다 (예: user@example.com)"}