; Please enter a commit message to explain why this merge is necessary,
; especially if it merges an updated upstream into a topic branch.
;
; Lines starting with ';' will be ignored, and an empty message aborts
; the commit.
This commit is contained in:
leeheejin
2026-01-08 17:10:04 +09:00
24 changed files with 1330 additions and 408 deletions

View File

@@ -1369,58 +1369,25 @@ export const InteractiveScreenViewer: React.FC<InteractiveScreenViewerProps> = (
}
case "entity": {
// DynamicWebTypeRenderer로 위임하여 EntitySearchInputWrapper 사용
const widget = comp as WidgetComponent;
const config = widget.webTypeConfig as EntityTypeConfig | undefined;
console.log("🏢 InteractiveScreenViewer - Entity 위젯:", {
componentId: widget.id,
widgetType: widget.widgetType,
config,
appliedSettings: {
entityName: config?.entityName,
displayField: config?.displayField,
valueField: config?.valueField,
multiple: config?.multiple,
defaultValue: config?.defaultValue,
},
});
const finalPlaceholder = config?.placeholder || "엔티티를 선택하세요...";
const defaultOptions = [
{ label: "사용자", value: "user" },
{ label: "제품", value: "product" },
{ label: "주문", value: "order" },
{ label: "카테고리", value: "category" },
];
return (
<Select
value={currentValue || config?.defaultValue || ""}
onValueChange={(value) => updateFormData(fieldName, value)}
disabled={readonly}
required={required}
>
<SelectTrigger
className="w-full"
style={{ height: "100%" }}
style={{
...comp.style,
width: "100%",
height: "100%",
}}
>
<SelectValue placeholder={finalPlaceholder} />
</SelectTrigger>
<SelectContent>
{defaultOptions.map((option) => (
<SelectItem key={option.value} value={option.value}>
{config?.displayFormat
? config.displayFormat.replace("{label}", option.label).replace("{value}", option.value)
: option.label}
</SelectItem>
))}
</SelectContent>
</Select>,
return applyStyles(
<DynamicWebTypeRenderer
webType="entity"
config={widget.webTypeConfig}
props={{
component: widget,
value: currentValue,
onChange: (value: any) => updateFormData(fieldName, value),
onFormDataChange: updateFormData,
formData: formData,
readonly: readonly,
required: required,
placeholder: widget.placeholder || "엔티티를 선택하세요",
isInteractive: true,
className: "w-full h-full",
}}
/>,
);
}

View File

@@ -6,6 +6,7 @@ import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Checkbox } from "@/components/ui/checkbox";
import { Switch } from "@/components/ui/switch";
import { Badge } from "@/components/ui/badge";
import { Search, Database, Link, X, Plus } from "lucide-react";
import { EntityTypeConfig } from "@/types/screen";
@@ -26,6 +27,8 @@ export const EntityTypeConfigPanel: React.FC<EntityTypeConfigPanelProps> = ({ co
placeholder: "",
displayFormat: "simple",
separator: " - ",
multiple: false, // 다중 선택
uiMode: "select", // UI 모드: select, combo, modal, autocomplete
...config,
};
@@ -38,6 +41,8 @@ export const EntityTypeConfigPanel: React.FC<EntityTypeConfigPanelProps> = ({ co
placeholder: safeConfig.placeholder,
displayFormat: safeConfig.displayFormat,
separator: safeConfig.separator,
multiple: safeConfig.multiple,
uiMode: safeConfig.uiMode,
});
const [newFilter, setNewFilter] = useState({ field: "", operator: "=", value: "" });
@@ -74,6 +79,8 @@ export const EntityTypeConfigPanel: React.FC<EntityTypeConfigPanelProps> = ({ co
placeholder: safeConfig.placeholder,
displayFormat: safeConfig.displayFormat,
separator: safeConfig.separator,
multiple: safeConfig.multiple,
uiMode: safeConfig.uiMode,
});
}, [
safeConfig.referenceTable,
@@ -83,8 +90,18 @@ export const EntityTypeConfigPanel: React.FC<EntityTypeConfigPanelProps> = ({ co
safeConfig.placeholder,
safeConfig.displayFormat,
safeConfig.separator,
safeConfig.multiple,
safeConfig.uiMode,
]);
// UI 모드 옵션
const uiModes = [
{ value: "select", label: "드롭다운 선택" },
{ value: "combo", label: "입력 + 모달 버튼" },
{ value: "modal", label: "모달 팝업" },
{ value: "autocomplete", label: "자동완성" },
];
const updateConfig = (key: keyof EntityTypeConfig, value: any) => {
// 로컬 상태 즉시 업데이트
setLocalValues((prev) => ({ ...prev, [key]: value }));
@@ -260,6 +277,46 @@ export const EntityTypeConfigPanel: React.FC<EntityTypeConfigPanelProps> = ({ co
/>
</div>
{/* UI 모드 */}
<div>
<Label htmlFor="uiMode" className="text-sm font-medium">
UI
</Label>
<Select value={localValues.uiMode || "select"} onValueChange={(value) => updateConfig("uiMode", value)}>
<SelectTrigger className="mt-1 h-8 w-full text-xs">
<SelectValue placeholder="모드 선택" />
</SelectTrigger>
<SelectContent>
{uiModes.map((mode) => (
<SelectItem key={mode.value} value={mode.value}>
{mode.label}
</SelectItem>
))}
</SelectContent>
</Select>
<p className="text-muted-foreground mt-1 text-xs">
{localValues.uiMode === "select" && "검색 가능한 드롭다운 형태로 표시됩니다."}
{localValues.uiMode === "combo" && "입력 필드와 검색 버튼이 함께 표시됩니다."}
{localValues.uiMode === "modal" && "모달 팝업에서 데이터를 검색하고 선택합니다."}
{localValues.uiMode === "autocomplete" && "입력하면서 자동완성 목록이 표시됩니다."}
</p>
</div>
{/* 다중 선택 */}
<div className="flex items-center justify-between rounded-md border p-3">
<div className="space-y-1">
<Label htmlFor="multiple" className="text-sm font-medium">
</Label>
<p className="text-muted-foreground text-xs"> .</p>
</div>
<Switch
id="multiple"
checked={localValues.multiple || false}
onCheckedChange={(checked) => updateConfig("multiple", checked)}
/>
</div>
{/* 필터 관리 */}
<div className="space-y-3">
<Label className="text-sm font-medium"> </Label>