diff --git a/frontend/lib/registry/pop-components/pop-field/PopFieldConfig.tsx b/frontend/lib/registry/pop-components/pop-field/PopFieldConfig.tsx index 8e0f8ad1..965bccee 100644 --- a/frontend/lib/registry/pop-components/pop-field/PopFieldConfig.tsx +++ b/frontend/lib/registry/pop-components/pop-field/PopFieldConfig.tsx @@ -356,7 +356,10 @@ function SaveTabContent({ }; const syncAndUpdateSaveMappings = useCallback( - (updater?: (prev: PopFieldSaveMapping[]) => PopFieldSaveMapping[]) => { + ( + updater?: (prev: PopFieldSaveMapping[]) => PopFieldSaveMapping[], + extraPartial?: Partial, + ) => { const fieldIds = new Set(allFields.map(({ field }) => field.id)); const prev = saveMappings.filter((m) => fieldIds.has(m.fieldId)); const next = updater ? updater(prev) : prev; @@ -381,6 +384,7 @@ function SaveTabContent({ tableName: saveTableName, fieldMappings: merged, }, + ...extraPartial, }); } }, @@ -395,22 +399,27 @@ function SaveTabContent({ const updateSaveMapping = useCallback( (fieldId: string, partial: Partial) => { - syncAndUpdateSaveMappings((prev) => - prev.map((m) => (m.fieldId === fieldId ? { ...m, ...partial } : m)) - ); + let extraPartial: Partial | undefined; if (partial.targetColumn !== undefined) { const newFieldName = partial.targetColumn || ""; - const sections = cfg.sections.map((s) => ({ - ...s, - fields: (s.fields ?? []).map((f) => - f.id === fieldId ? { ...f, fieldName: newFieldName } : f - ), - })); - onUpdateConfig({ sections }); + extraPartial = { + sections: cfg.sections.map((s) => ({ + ...s, + fields: (s.fields ?? []).map((f) => + f.id === fieldId ? { ...f, fieldName: newFieldName } : f + ), + })), + }; } + + syncAndUpdateSaveMappings( + (prev) => + prev.map((m) => (m.fieldId === fieldId ? { ...m, ...partial } : m)), + extraPartial, + ); }, - [syncAndUpdateSaveMappings, cfg, onUpdateConfig] + [syncAndUpdateSaveMappings, cfg.sections] ); // --- 숨은 필드 매핑 로직 --- @@ -2086,23 +2095,24 @@ function JsonKeySelect({ onOpen?: () => void; }) { const [open, setOpen] = useState(false); + const [inputValue, setInputValue] = useState(""); const handleOpenChange = (nextOpen: boolean) => { setOpen(nextOpen); - if (nextOpen) onOpen?.(); + if (nextOpen) { + onOpen?.(); + setInputValue(""); + } }; - if (keys.length === 0 && !value) { - return ( - onValueChange(e.target.value)} - onFocus={() => onOpen?.()} - className="h-7 w-24 text-xs" - /> - ); - } + const handleInputKeyDown = (e: React.KeyboardEvent) => { + if (e.key === "Enter" && inputValue.trim()) { + e.preventDefault(); + onValueChange(inputValue.trim()); + setInputValue(""); + setOpen(false); + } + }; return ( @@ -2117,33 +2127,51 @@ function JsonKeySelect({ - - + 0}> + 0 ? "키 검색..." : "키 직접 입력..."} + className="text-xs" + value={inputValue} + onValueChange={setInputValue} + onKeyDown={handleInputKeyDown} + /> - - {keys.length === 0 ? "데이터를 불러오는 중..." : "일치하는 키가 없습니다."} - - - {keys.map((k) => ( - { - onValueChange(v === value ? "" : v); - setOpen(false); - }} - className="text-xs" - > - - {k} - - ))} - + {keys.length === 0 ? ( +
+ {inputValue.trim() + ? "Enter로 입력 확정" + : "테이블에 데이터가 없습니다. 키를 직접 입력하세요."} +
+ ) : ( + <> + + {inputValue.trim() + ? "Enter로 직접 입력 확정" + : "일치하는 키가 없습니다."} + + + {keys.map((k) => ( + { + onValueChange(v === value ? "" : v); + setOpen(false); + }} + className="text-xs" + > + + {k} + + ))} + + + )}