fix: update V2PropertiesPanel to use V2FieldConfigPanel for input and select components

- Replaced references to V2InputConfigPanel and V2SelectConfigPanel with V2FieldConfigPanel in the V2PropertiesPanel.
- This change ensures consistent configuration handling for both input and select components, improving maintainability and usability.

Made-with: Cursor
This commit is contained in:
kjs 2026-03-12 15:46:54 +09:00
parent 7da04c6a09
commit 772a10258c
1 changed files with 122 additions and 53 deletions

View File

@ -183,6 +183,62 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
selectedComponent.componentConfig?.id ||
(selectedComponent.type === "component" ? selectedComponent.id : null); // 🆕 독립 컴포넌트 (table-search-widget 등)
// 🆕 V2 컴포넌트 직접 감지 및 설정 패널 렌더링
if (componentId?.startsWith("v2-")) {
const v2ConfigPanels: Record<string, React.FC<{ config: any; onChange: (config: any) => void }>> = {
"v2-input": require("@/components/v2/config-panels/V2FieldConfigPanel").V2FieldConfigPanel,
"v2-select": require("@/components/v2/config-panels/V2FieldConfigPanel").V2FieldConfigPanel,
"v2-date": require("@/components/v2/config-panels/V2DateConfigPanel").V2DateConfigPanel,
"v2-list": require("@/components/v2/config-panels/V2ListConfigPanel").V2ListConfigPanel,
"v2-layout": require("@/components/v2/config-panels/V2LayoutConfigPanel").V2LayoutConfigPanel,
"v2-group": require("@/components/v2/config-panels/V2GroupConfigPanel").V2GroupConfigPanel,
"v2-media": require("@/components/v2/config-panels/V2MediaConfigPanel").V2MediaConfigPanel,
"v2-biz": require("@/components/v2/config-panels/V2BizConfigPanel").V2BizConfigPanel,
"v2-hierarchy": require("@/components/v2/config-panels/V2HierarchyConfigPanel").V2HierarchyConfigPanel,
"v2-bom-item-editor": require("@/components/v2/config-panels/V2BomItemEditorConfigPanel")
.V2BomItemEditorConfigPanel,
"v2-bom-tree": require("@/components/v2/config-panels/V2BomTreeConfigPanel").V2BomTreeConfigPanel,
};
const V2ConfigPanel = v2ConfigPanels[componentId];
if (V2ConfigPanel) {
const currentConfig = selectedComponent.componentConfig || {};
const handleV2ConfigChange = (newConfig: any) => {
onUpdateProperty(selectedComponent.id, "componentConfig", { ...currentConfig, ...newConfig });
};
// 컬럼의 inputType 가져오기 (entity 타입인지 확인용)
const inputType = currentConfig.inputType || currentConfig.webType || (selectedComponent as any).inputType;
// 현재 화면의 테이블명 가져오기
const currentTableName = tables?.[0]?.tableName;
// 컴포넌트별 추가 props
const extraProps: Record<string, any> = {};
if (componentId === "v2-select") {
extraProps.inputType = inputType;
extraProps.tableName = selectedComponent.tableName || currentTable?.tableName || currentTableName;
extraProps.columnName = selectedComponent.columnName || currentConfig.fieldKey || currentConfig.columnName;
}
if (componentId === "v2-list") {
extraProps.currentTableName = currentTableName;
}
if (componentId === "v2-bom-item-editor" || componentId === "v2-bom-tree") {
extraProps.currentTableName = currentTableName;
extraProps.screenTableName = selectedComponent.tableName || currentTable?.tableName || currentTableName;
}
if (componentId === "v2-input") {
extraProps.allComponents = allComponents;
}
return (
<div key={selectedComponent.id} className="space-y-4">
<V2ConfigPanel config={currentConfig} onChange={handleV2ConfigChange} {...extraProps} />
</div>
);
}
}
if (componentId) {
const definition = ComponentRegistry.getComponent(componentId);
@ -219,7 +275,9 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
allTables={allTables}
screenTableName={selectedComponent.tableName || currentTable?.tableName || currentTableName}
tableName={selectedComponent.tableName || currentTable?.tableName || currentTableName}
columnName={(selectedComponent as any).columnName || currentConfig?.columnName || currentConfig?.fieldName}
columnName={
(selectedComponent as any).columnName || currentConfig?.columnName || currentConfig?.fieldName
}
inputType={(selectedComponent as any).inputType || currentConfig?.inputType}
componentType={componentType}
tableColumns={currentTable?.columns || []}
@ -334,11 +392,11 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
return (
<div className="space-y-1">
{/* DIMENSIONS 섹션 */}
<div className="border-b border-border/50 pb-3 mb-3">
<h4 className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground py-2">DIMENSIONS</h4>
<div className="border-border/50 mb-3 border-b pb-3">
<h4 className="text-muted-foreground py-2 text-[10px] font-semibold tracking-wider uppercase">DIMENSIONS</h4>
<div className="flex gap-2">
<div className="flex-1">
<Label className="text-[10px] text-muted-foreground"></Label>
<Label className="text-muted-foreground text-[10px]"></Label>
<Input
type="number"
min={10}
@ -372,7 +430,7 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
/>
</div>
<div className="flex-1">
<Label className="text-[10px] text-muted-foreground"></Label>
<Label className="text-muted-foreground text-[10px]"></Label>
<Input
type="number"
value={localHeight}
@ -404,7 +462,7 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
/>
</div>
<div className="flex-1">
<Label className="text-[10px] text-muted-foreground">Z-Index</Label>
<Label className="text-muted-foreground text-[10px]">Z-Index</Label>
<Input
type="number"
step="1"
@ -418,10 +476,10 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
{/* Title (group/area) */}
{(selectedComponent.type === "group" || selectedComponent.type === "area") && (
<div className="border-b border-border/50 pb-3 mb-3">
<h4 className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground py-2">CONTENT</h4>
<div className="border-border/50 mb-3 border-b pb-3">
<h4 className="text-muted-foreground py-2 text-[10px] font-semibold tracking-wider uppercase">CONTENT</h4>
<div className="flex items-center justify-between py-1.5">
<span className="text-xs text-muted-foreground"></span>
<span className="text-muted-foreground text-xs"></span>
<div className="w-[160px]">
<Input
value={group.title || area.title || ""}
@ -433,7 +491,7 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
</div>
{selectedComponent.type === "area" && (
<div className="flex items-center justify-between py-1.5">
<span className="text-xs text-muted-foreground"></span>
<span className="text-muted-foreground text-xs"></span>
<div className="w-[160px]">
<Input
value={area.description || ""}
@ -448,36 +506,46 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
)}
{/* OPTIONS 섹션 */}
<div className="border-b border-border/50 pb-3 mb-3">
<h4 className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground py-2">OPTIONS</h4>
{(isInputField || widget.required !== undefined) && (() => {
const colName = widget.columnName || selectedComponent?.columnName;
const colMeta = colName ? currentTable?.columns?.find(
(c: any) => (c.columnName || c.column_name || "").toLowerCase() === colName.toLowerCase()
) : null;
const isNotNull = colMeta && ((colMeta as any).isNullable === "NO" || (colMeta as any).isNullable === "N" || (colMeta as any).is_nullable === "NO" || (colMeta as any).is_nullable === "N");
return (
<div className="flex items-center justify-between py-1.5">
<span className="text-xs text-muted-foreground">
{isNotNull && <span className="text-muted-foreground/60 ml-1">(NOT NULL)</span>}
</span>
<Checkbox
checked={isNotNull || widget.required === true || selectedComponent.componentConfig?.required === true}
onCheckedChange={(checked) => {
if (isNotNull) return;
handleUpdate("required", checked);
handleUpdate("componentConfig.required", checked);
}}
disabled={!!isNotNull}
className="h-4 w-4"
/>
</div>
);
})()}
<div className="border-border/50 mb-3 border-b pb-3">
<h4 className="text-muted-foreground py-2 text-[10px] font-semibold tracking-wider uppercase">OPTIONS</h4>
{(isInputField || widget.required !== undefined) &&
(() => {
const colName = widget.columnName || selectedComponent?.columnName;
const colMeta = colName
? currentTable?.columns?.find(
(c: any) => (c.columnName || c.column_name || "").toLowerCase() === colName.toLowerCase(),
)
: null;
const isNotNull =
colMeta &&
((colMeta as any).isNullable === "NO" ||
(colMeta as any).isNullable === "N" ||
(colMeta as any).is_nullable === "NO" ||
(colMeta as any).is_nullable === "N");
return (
<div className="flex items-center justify-between py-1.5">
<span className="text-muted-foreground text-xs">
{isNotNull && <span className="text-muted-foreground/60 ml-1">(NOT NULL)</span>}
</span>
<Checkbox
checked={
isNotNull || widget.required === true || selectedComponent.componentConfig?.required === true
}
onCheckedChange={(checked) => {
if (isNotNull) return;
handleUpdate("required", checked);
handleUpdate("componentConfig.required", checked);
}}
disabled={!!isNotNull}
className="h-4 w-4"
/>
</div>
);
})()}
{(isInputField || widget.readonly !== undefined) && (
<div className="flex items-center justify-between py-1.5">
<span className="text-xs text-muted-foreground"></span>
<span className="text-muted-foreground text-xs"></span>
<Checkbox
checked={widget.readonly === true || selectedComponent.componentConfig?.readonly === true}
onCheckedChange={(checked) => {
@ -489,7 +557,7 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
</div>
)}
<div className="flex items-center justify-between py-1.5">
<span className="text-xs text-muted-foreground"></span>
<span className="text-muted-foreground text-xs"></span>
<Checkbox
checked={selectedComponent.hidden === true || selectedComponent.componentConfig?.hidden === true}
onCheckedChange={(checked) => {
@ -505,13 +573,13 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
{isInputField && (
<Collapsible>
<CollapsibleTrigger className="flex w-full items-center justify-between py-0.5 text-left">
<span className="text-[10px] font-semibold uppercase tracking-wider text-muted-foreground">LABEL</span>
<ChevronDown className="h-3 w-3 shrink-0 text-muted-foreground/50" />
<span className="text-muted-foreground text-[10px] font-semibold tracking-wider uppercase">LABEL</span>
<ChevronDown className="text-muted-foreground/50 h-3 w-3 shrink-0" />
</CollapsibleTrigger>
<CollapsibleContent className="mt-1.5 space-y-1">
{/* 라벨 텍스트 */}
<div className="flex items-center justify-between py-1.5">
<span className="text-xs text-muted-foreground"></span>
<span className="text-muted-foreground text-xs"></span>
<div className="w-[160px]">
<Input
value={
@ -531,7 +599,7 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
{/* 위치 + 간격 */}
<div className="flex gap-2">
<div className="flex-1">
<Label className="text-[10px] text-muted-foreground"></Label>
<Label className="text-muted-foreground text-[10px]"></Label>
<Select
value={selectedComponent.style?.labelPosition || "top"}
onValueChange={(value) => handleUpdate("style.labelPosition", value)}
@ -548,12 +616,13 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
</Select>
</div>
<div className="flex-1">
<Label className="text-[10px] text-muted-foreground"></Label>
<Label className="text-muted-foreground text-[10px]"></Label>
<Input
value={
(selectedComponent.style?.labelPosition === "left" || selectedComponent.style?.labelPosition === "right")
? (selectedComponent.style?.labelGap || "8px")
: (selectedComponent.style?.labelMarginBottom || "4px")
selectedComponent.style?.labelPosition === "left" ||
selectedComponent.style?.labelPosition === "right"
? selectedComponent.style?.labelGap || "8px"
: selectedComponent.style?.labelMarginBottom || "4px"
}
onChange={(e) => {
const pos = selectedComponent.style?.labelPosition;
@ -570,7 +639,7 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
{/* 크기 + 색상 */}
<div className="flex gap-2">
<div className="flex-1">
<Label className="text-[10px] text-muted-foreground"></Label>
<Label className="text-muted-foreground text-[10px]"></Label>
<Input
value={selectedComponent.style?.labelFontSize || "12px"}
onChange={(e) => handleUpdate("style.labelFontSize", e.target.value)}
@ -578,7 +647,7 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
/>
</div>
<div className="flex-1">
<Label className="text-[10px] text-muted-foreground"></Label>
<Label className="text-muted-foreground text-[10px]"></Label>
<ColorPickerWithTransparent
value={selectedComponent.style?.labelColor}
onChange={(value) => handleUpdate("style.labelColor", value)}
@ -589,7 +658,7 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
</div>
{/* 굵기 */}
<div className="flex items-center justify-between py-1.5">
<span className="text-xs text-muted-foreground"></span>
<span className="text-muted-foreground text-xs"></span>
<div className="w-[160px]">
<Select
value={selectedComponent.style?.labelFontWeight || "500"}
@ -609,7 +678,7 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
</div>
{/* 표시 */}
<div className="flex items-center justify-between py-1.5">
<span className="text-xs text-muted-foreground"></span>
<span className="text-muted-foreground text-xs"></span>
<Checkbox
checked={selectedComponent.style?.labelDisplay === true || selectedComponent.labelDisplay === true}
onCheckedChange={(checked) => {
@ -965,7 +1034,7 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
{/* 🆕 테이블 데이터 자동 입력 (모든 widget 컴포넌트) */}
<Separator />
<div className="space-y-3 border-4 border-destructive bg-amber-100 p-4">
<div className="border-destructive space-y-3 border-4 bg-amber-100 p-4">
<div className="flex items-center gap-2">
<Database className="text-primary h-4 w-4" />
<h4 className="text-xs font-semibold"> </h4>
@ -1134,7 +1203,7 @@ export const V2PropertiesPanel: React.FC<V2PropertiesPanelProps> = ({
<Zap className="text-primary h-3 w-3" />
<h4 className="text-xs font-semibold"> </h4>
</div>
<div className="rounded-md border border-border p-2">
<div className="border-border rounded-md border p-2">
<ConditionalConfigPanel
config={
(selectedComponent as any).conditional || {