diff --git a/frontend/components/screen/SaveModal.tsx b/frontend/components/screen/SaveModal.tsx index d1e942ff..4228f389 100644 --- a/frontend/components/screen/SaveModal.tsx +++ b/frontend/components/screen/SaveModal.tsx @@ -337,12 +337,22 @@ export const SaveModal: React.FC = ({ formData={formData} originalData={originalData} onFormDataChange={(fieldName, value) => { - setFormData((prev) => ({ - ...prev, - [fieldName]: value, - })); + console.log("πŸ“ SaveModal - formData λ³€κ²½:", { + fieldName, + value, + componentType: component.type, + componentId: component.id, + }); + setFormData((prev) => { + const newData = { + ...prev, + [fieldName]: value, + }; + console.log("πŸ“¦ μƒˆ formData:", newData); + return newData; + }); }} - mode={initialData ? "edit" : "create"} + mode="edit" isInModal={true} isInteractive={true} /> diff --git a/frontend/lib/registry/components/autocomplete-search-input/AutocompleteSearchInputComponent.tsx b/frontend/lib/registry/components/autocomplete-search-input/AutocompleteSearchInputComponent.tsx index 42baabdc..263cb294 100644 --- a/frontend/lib/registry/components/autocomplete-search-input/AutocompleteSearchInputComponent.tsx +++ b/frontend/lib/registry/components/autocomplete-search-input/AutocompleteSearchInputComponent.tsx @@ -7,18 +7,23 @@ import { Button } from "@/components/ui/button"; import { useEntitySearch } from "../entity-search-input/useEntitySearch"; import { EntitySearchResult } from "../entity-search-input/types"; import { cn } from "@/lib/utils"; -import { AutocompleteSearchInputConfig, FieldMapping } from "./types"; +import { AutocompleteSearchInputConfig } from "./types"; +import { ComponentRendererProps } from "../../DynamicComponentRenderer"; -interface AutocompleteSearchInputProps extends Partial { +export interface AutocompleteSearchInputProps extends ComponentRendererProps { config?: AutocompleteSearchInputConfig; + tableName?: string; + displayField?: string; + valueField?: string; + searchFields?: string[]; filterCondition?: Record; - disabled?: boolean; - value?: any; - onChange?: (value: any, fullData?: any) => void; - className?: string; + placeholder?: string; + showAdditionalInfo?: boolean; + additionalFields?: string[]; } export function AutocompleteSearchInputComponent({ + component, config, tableName: propTableName, displayField: propDisplayField, @@ -29,9 +34,10 @@ export function AutocompleteSearchInputComponent({ disabled = false, value, onChange, - showAdditionalInfo: propShowAdditionalInfo, - additionalFields: propAdditionalFields, className, + isInteractive = false, + onFormDataChange, + formData, }: AutocompleteSearchInputProps) { // config prop μš°μ„ , μ—†μœΌλ©΄ κ°œλ³„ prop μ‚¬μš© const tableName = config?.tableName || propTableName || ""; @@ -39,8 +45,7 @@ export function AutocompleteSearchInputComponent({ const valueField = config?.valueField || propValueField || ""; const searchFields = config?.searchFields || propSearchFields || [displayField]; const placeholder = config?.placeholder || propPlaceholder || "검색..."; - const showAdditionalInfo = config?.showAdditionalInfo ?? propShowAdditionalInfo ?? false; - const additionalFields = config?.additionalFields || propAdditionalFields || []; + const [inputValue, setInputValue] = useState(""); const [isOpen, setIsOpen] = useState(false); const [selectedData, setSelectedData] = useState(null); @@ -52,15 +57,20 @@ export function AutocompleteSearchInputComponent({ filterCondition, }); + // formDataμ—μ„œ ν˜„μž¬ κ°’ κ°€μ Έμ˜€κΈ° (isInteractive λͺ¨λ“œ) + const currentValue = isInteractive && formData && component?.columnName + ? formData[component.columnName] + : value; + // valueκ°€ λ³€κ²½λ˜λ©΄ ν‘œμ‹œκ°’ μ—…λ°μ΄νŠΈ useEffect(() => { - if (value && selectedData) { + if (currentValue && selectedData) { setInputValue(selectedData[displayField] || ""); - } else if (!value) { + } else if (!currentValue) { setInputValue(""); setSelectedData(null); } - }, [value, displayField]); + }, [currentValue, displayField, selectedData]); // μ™ΈλΆ€ 클릭 감지 useEffect(() => { @@ -81,45 +91,61 @@ export function AutocompleteSearchInputComponent({ setIsOpen(true); }; - // ν•„λ“œ μžλ™ λ§€ν•‘ 처리 - const applyFieldMappings = (item: EntitySearchResult) => { - if (!config?.enableFieldMapping || !config?.fieldMappings) { - return; - } - - config.fieldMappings.forEach((mapping: FieldMapping) => { - if (!mapping.sourceField || !mapping.targetField) { - return; - } - - const value = item[mapping.sourceField]; - - // DOMμ—μ„œ νƒ€κ²Ÿ ν•„λ“œ μ°ΎκΈ° (id둜 검색) - const targetElement = document.getElementById(mapping.targetField); - - if (targetElement) { - // input, textarea λ“±μ˜ κ°’ μ„€μ • - if ( - targetElement instanceof HTMLInputElement || - targetElement instanceof HTMLTextAreaElement - ) { - targetElement.value = value?.toString() || ""; - - // React의 change 이벀트 트리거 - const event = new Event("input", { bubbles: true }); - targetElement.dispatchEvent(event); - } - } - }); - }; - const handleSelect = (item: EntitySearchResult) => { setSelectedData(item); setInputValue(item[displayField] || ""); - onChange?.(item[valueField], item); - // ν•„λ“œ μžλ™ λ§€ν•‘ μ‹€ν–‰ - applyFieldMappings(item); + console.log("πŸ” AutocompleteSearchInput handleSelect:", { + item, + valueField, + value: item[valueField], + config, + isInteractive, + hasOnFormDataChange: !!onFormDataChange, + columnName: component?.columnName, + }); + + // isInteractive λͺ¨λ“œμ—μ„œλ§Œ μ €μž₯ + if (isInteractive && onFormDataChange) { + // ν•„λ“œ λ§€ν•‘ 처리 + if (config?.fieldMappings && Array.isArray(config.fieldMappings)) { + console.log("πŸ“‹ ν•„λ“œ λ§€ν•‘ 처리 μ‹œμž‘:", config.fieldMappings); + + config.fieldMappings.forEach((mapping: any, index: number) => { + const targetField = mapping.targetField || mapping.targetColumn; + + console.log(` λ§€ν•‘ ${index + 1}:`, { + sourceField: mapping.sourceField, + targetField, + label: mapping.label, + }); + + if (mapping.sourceField && targetField) { + const sourceValue = item[mapping.sourceField]; + + console.log(` κ°’: ${mapping.sourceField} = ${sourceValue}`); + + if (sourceValue !== undefined) { + console.log(` βœ… μ €μž₯: ${targetField} = ${sourceValue}`); + onFormDataChange(targetField, sourceValue); + } else { + console.warn(` ⚠️ sourceField "${mapping.sourceField}"의 값이 undefinedμž…λ‹ˆλ‹€`); + } + } else { + console.warn(` ⚠️ λ§€ν•‘ λΆˆμ™„μ „: sourceField=${mapping.sourceField}, targetField=${targetField}`); + } + }); + } + + // κΈ°λ³Έ ν•„λ“œ μ €μž₯ (columnName이 μ„€μ •λœ 경우) + if (component?.columnName) { + console.log(`πŸ’Ύ κΈ°λ³Έ ν•„λ“œ μ €μž₯: ${component.columnName} = ${item[valueField]}`); + onFormDataChange(component.columnName, item[valueField]); + } + } + + // onChange 콜백 호좜 (ν˜Έν™˜μ„±) + onChange?.(item[valueField], item); setIsOpen(false); }; @@ -149,9 +175,9 @@ export function AutocompleteSearchInputComponent({ onFocus={handleInputFocus} placeholder={placeholder} disabled={disabled} - className="h-8 text-xs sm:h-10 sm:text-sm pr-16" + className="h-8 pr-16 text-xs sm:h-10 sm:text-sm" /> -
+
{loading && ( )} @@ -172,10 +198,10 @@ export function AutocompleteSearchInputComponent({ {/* λ“œλ‘­λ‹€μš΄ κ²°κ³Ό */} {isOpen && (results.length > 0 || loading) && ( -
+
{loading && results.length === 0 ? (
- + 검색 쀑...
) : results.length === 0 ? ( @@ -189,37 +215,15 @@ export function AutocompleteSearchInputComponent({ key={index} type="button" onClick={() => handleSelect(item)} - className="w-full text-left px-3 py-2 hover:bg-accent text-xs sm:text-sm transition-colors" + className="w-full px-3 py-2 text-left text-xs transition-colors hover:bg-accent sm:text-sm" >
{item[displayField]}
- {additionalFields.length > 0 && ( -
- {additionalFields.map((field) => ( -
- {field}: {item[field] || "-"} -
- ))} -
- )} ))}
)}
)} - - {/* μΆ”κ°€ 정보 ν‘œμ‹œ */} - {showAdditionalInfo && selectedData && additionalFields.length > 0 && ( -
- {additionalFields.map((field) => ( -
- {field}: - {selectedData[field] || "-"} -
- ))} -
- )}
); } - diff --git a/frontend/lib/registry/components/autocomplete-search-input/AutocompleteSearchInputConfigPanel.tsx b/frontend/lib/registry/components/autocomplete-search-input/AutocompleteSearchInputConfigPanel.tsx index 96a212b1..e6942704 100644 --- a/frontend/lib/registry/components/autocomplete-search-input/AutocompleteSearchInputConfigPanel.tsx +++ b/frontend/lib/registry/components/autocomplete-search-input/AutocompleteSearchInputConfigPanel.tsx @@ -6,10 +6,9 @@ import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; -import { Switch } from "@/components/ui/switch"; import { Button } from "@/components/ui/button"; import { Plus, X, Check, ChevronsUpDown } from "lucide-react"; -import { AutocompleteSearchInputConfig, FieldMapping, ValueFieldStorage } from "./types"; +import { AutocompleteSearchInputConfig } from "./types"; import { tableManagementApi } from "@/lib/api/tableManagement"; import { cn } from "@/lib/utils"; @@ -24,83 +23,14 @@ export function AutocompleteSearchInputConfigPanel({ }: AutocompleteSearchInputConfigPanelProps) { const [localConfig, setLocalConfig] = useState(config); const [allTables, setAllTables] = useState([]); - const [tableColumns, setTableColumns] = useState([]); + const [sourceTableColumns, setSourceTableColumns] = useState([]); + const [targetTableColumns, setTargetTableColumns] = useState([]); const [isLoadingTables, setIsLoadingTables] = useState(false); - const [isLoadingColumns, setIsLoadingColumns] = useState(false); - const [openTableCombo, setOpenTableCombo] = useState(false); + const [isLoadingSourceColumns, setIsLoadingSourceColumns] = useState(false); + const [isLoadingTargetColumns, setIsLoadingTargetColumns] = useState(false); + const [openSourceTableCombo, setOpenSourceTableCombo] = useState(false); + const [openTargetTableCombo, setOpenTargetTableCombo] = useState(false); const [openDisplayFieldCombo, setOpenDisplayFieldCombo] = useState(false); - const [openValueFieldCombo, setOpenValueFieldCombo] = useState(false); - const [openStorageTableCombo, setOpenStorageTableCombo] = useState(false); - const [openStorageColumnCombo, setOpenStorageColumnCombo] = useState(false); - const [storageTableColumns, setStorageTableColumns] = useState([]); - const [isLoadingStorageColumns, setIsLoadingStorageColumns] = useState(false); - - // 전체 ν…Œμ΄λΈ” λͺ©λ‘ λ‘œλ“œ - useEffect(() => { - const loadTables = async () => { - setIsLoadingTables(true); - try { - const response = await tableManagementApi.getTableList(); - if (response.success && response.data) { - setAllTables(response.data); - } - } catch (error) { - console.error("ν…Œμ΄λΈ” λͺ©λ‘ λ‘œλ“œ μ‹€νŒ¨:", error); - } finally { - setIsLoadingTables(false); - } - }; - loadTables(); - }, []); - - // μ„ νƒλœ ν…Œμ΄λΈ”μ˜ 컬럼 λͺ©λ‘ λ‘œλ“œ - useEffect(() => { - const loadColumns = async () => { - if (!localConfig.tableName) { - setTableColumns([]); - return; - } - - setIsLoadingColumns(true); - try { - const response = await tableManagementApi.getColumnList(localConfig.tableName); - if (response.success && response.data) { - setTableColumns(response.data.columns); - } - } catch (error) { - console.error("컬럼 λͺ©λ‘ λ‘œλ“œ μ‹€νŒ¨:", error); - setTableColumns([]); - } finally { - setIsLoadingColumns(false); - } - }; - loadColumns(); - }, [localConfig.tableName]); - - // μ €μž₯ λŒ€μƒ ν…Œμ΄λΈ”μ˜ 컬럼 λͺ©λ‘ λ‘œλ“œ - useEffect(() => { - const loadStorageColumns = async () => { - const storageTable = localConfig.valueFieldStorage?.targetTable; - if (!storageTable) { - setStorageTableColumns([]); - return; - } - - setIsLoadingStorageColumns(true); - try { - const response = await tableManagementApi.getColumnList(storageTable); - if (response.success && response.data) { - setStorageTableColumns(response.data.columns); - } - } catch (error) { - console.error("μ €μž₯ ν…Œμ΄λΈ” 컬럼 λ‘œλ“œ μ‹€νŒ¨:", error); - setStorageTableColumns([]); - } finally { - setIsLoadingStorageColumns(false); - } - }; - loadStorageColumns(); - }, [localConfig.valueFieldStorage?.targetTable]); useEffect(() => { setLocalConfig(config); @@ -112,52 +42,76 @@ export function AutocompleteSearchInputConfigPanel({ onConfigChange(newConfig); }; - const addSearchField = () => { - const fields = localConfig.searchFields || []; - updateConfig({ searchFields: [...fields, ""] }); - }; + // ν…Œμ΄λΈ” λͺ©λ‘ λ‘œλ“œ + useEffect(() => { + const loadTables = async () => { + setIsLoadingTables(true); + try { + const response = await tableManagementApi.getTableList(); + if (response.success && response.data) { + setAllTables(response.data); + } + } catch (error) { + setAllTables([]); + } finally { + setIsLoadingTables(false); + } + }; + loadTables(); + }, []); - const updateSearchField = (index: number, value: string) => { - const fields = [...(localConfig.searchFields || [])]; - fields[index] = value; - updateConfig({ searchFields: fields }); - }; + // μ™ΈλΆ€ ν…Œμ΄λΈ” 컬럼 λ‘œλ“œ + useEffect(() => { + const loadColumns = async () => { + if (!localConfig.tableName) { + setSourceTableColumns([]); + return; + } + setIsLoadingSourceColumns(true); + try { + const response = await tableManagementApi.getColumnList(localConfig.tableName); + if (response.success && response.data) { + setSourceTableColumns(response.data.columns); + } + } catch (error) { + setSourceTableColumns([]); + } finally { + setIsLoadingSourceColumns(false); + } + }; + loadColumns(); + }, [localConfig.tableName]); - const removeSearchField = (index: number) => { - const fields = [...(localConfig.searchFields || [])]; - fields.splice(index, 1); - updateConfig({ searchFields: fields }); - }; + // μ €μž₯ ν…Œμ΄λΈ” 컬럼 λ‘œλ“œ + useEffect(() => { + const loadTargetColumns = async () => { + if (!localConfig.targetTable) { + setTargetTableColumns([]); + return; + } + setIsLoadingTargetColumns(true); + try { + const response = await tableManagementApi.getColumnList(localConfig.targetTable); + if (response.success && response.data) { + setTargetTableColumns(response.data.columns); + } + } catch (error) { + setTargetTableColumns([]); + } finally { + setIsLoadingTargetColumns(false); + } + }; + loadTargetColumns(); + }, [localConfig.targetTable]); - const addAdditionalField = () => { - const fields = localConfig.additionalFields || []; - updateConfig({ additionalFields: [...fields, ""] }); - }; - - const updateAdditionalField = (index: number, value: string) => { - const fields = [...(localConfig.additionalFields || [])]; - fields[index] = value; - updateConfig({ additionalFields: fields }); - }; - - const removeAdditionalField = (index: number) => { - const fields = [...(localConfig.additionalFields || [])]; - fields.splice(index, 1); - updateConfig({ additionalFields: fields }); - }; - - // ν•„λ“œ λ§€ν•‘ 관리 ν•¨μˆ˜ const addFieldMapping = () => { const mappings = localConfig.fieldMappings || []; updateConfig({ - fieldMappings: [ - ...mappings, - { sourceField: "", targetField: "", label: "" }, - ], + fieldMappings: [...mappings, { sourceField: "", targetField: "", label: "" }], }); }; - const updateFieldMapping = (index: number, updates: Partial) => { + const updateFieldMapping = (index: number, updates: any) => { const mappings = [...(localConfig.fieldMappings || [])]; mappings[index] = { ...mappings[index], ...updates }; updateConfig({ fieldMappings: mappings }); @@ -170,21 +124,22 @@ export function AutocompleteSearchInputConfigPanel({ }; return ( -
+
+ {/* 1. μ™ΈλΆ€ ν…Œμ΄λΈ” 선택 */}
- - + + @@ -200,7 +155,7 @@ export function AutocompleteSearchInputConfigPanel({ value={table.tableName} onSelect={() => { updateConfig({ tableName: table.tableName }); - setOpenTableCombo(false); + setOpenSourceTableCombo(false); }} className="text-xs sm:text-sm" > @@ -216,13 +171,11 @@ export function AutocompleteSearchInputConfigPanel({ -

- 검색할 데이터가 μ €μž₯된 ν…Œμ΄λΈ”μ„ μ„ νƒν•˜μ„Έμš” -

+ {/* 2. ν‘œμ‹œ ν•„λ“œ 선택 */}
- + @@ -244,7 +197,7 @@ export function AutocompleteSearchInputConfigPanel({ ν•„λ“œλ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€. - {tableColumns.map((column) => ( + {sourceTableColumns.map((column) => ( -

- μ‚¬μš©μžμ—κ²Œ 보여쀄 ν•„λ“œ (예: 거래처λͺ…) -

+ {/* 3. μ €μž₯ λŒ€μƒ ν…Œμ΄λΈ” 선택 */}
- - + + - + - ν•„λ“œλ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€. + ν…Œμ΄λΈ”μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€. - {tableColumns.map((column) => ( + {allTables.map((table) => ( { - updateConfig({ valueField: column.columnName }); - setOpenValueFieldCombo(false); + updateConfig({ targetTable: table.tableName }); + setOpenTargetTableCombo(false); }} className="text-xs sm:text-sm" > - +
- {column.displayName || column.columnName} - {column.displayName && {column.columnName}} + {table.displayName || table.tableName} + {table.displayName && {table.tableName}}
))} @@ -316,11 +267,124 @@ export function AutocompleteSearchInputConfigPanel({
-

- 검색 ν…Œμ΄λΈ”μ—μ„œ κ°€μ Έμ˜¬ κ°’μ˜ 컬럼 (예: customer_code) -

+ {/* 4. ν•„λ“œ λ§€ν•‘ */} +
+
+ + +
+ + {(localConfig.fieldMappings || []).length === 0 && ( +
+

+ λ§€ν•‘ μΆ”κ°€ λ²„νŠΌμ„ 눌러 ν•„λ“œ 맀핑을 μ„€μ •ν•˜μ„Έμš” +

+
+ )} + +
+ {(localConfig.fieldMappings || []).map((mapping, index) => ( +
+
+ + λ§€ν•‘ #{index + 1} + + +
+ +
+ + + updateFieldMapping(index, { label: e.target.value }) + } + placeholder="예: 거래처 μ½”λ“œ" + className="h-8 text-xs sm:h-10 sm:text-sm" + /> +
+ +
+ + +
+ +
+ + +
+ + {mapping.sourceField && mapping.targetField && ( +
+

+ + {localConfig.tableName}.{mapping.sourceField} + + {" β†’ "} + + {localConfig.targetTable}.{mapping.targetField} + +

+
+ )} +
+ ))} +
+
+ + {/* ν”Œλ ˆμ΄μŠ€ν™€λ” */}
- {/* κ°’ ν•„λ“œ μ €μž₯ μœ„μΉ˜ μ„€μ • */} -
-
-

κ°’ ν•„λ“œ μ €μž₯ μœ„μΉ˜ (κ³ κΈ‰)

-

- μœ„μ—μ„œ μ„ νƒν•œ "κ°’ ν•„λ“œ"의 데이터λ₯Ό μ–΄λŠ ν…Œμ΄λΈ”/μ»¬λŸΌμ— μ €μž₯ν• μ§€ μ§€μ •ν•©λ‹ˆλ‹€. -
- λ―Έμ„€μ • μ‹œ ν™”λ©΄μ˜ μ—°κ²° ν…Œμ΄λΈ”μ— μ»΄ν¬λ„ŒνŠΈμ˜ 바인딩 ν•„λ“œλ‘œ μžλ™ μ €μž₯λ©λ‹ˆλ‹€. -

-
- - {/* μ €μž₯ ν…Œμ΄λΈ” 선택 */} -
- - - - - - - - - - ν…Œμ΄λΈ”μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€. - - {/* κΈ°λ³Έκ°’ μ˜΅μ…˜ */} - { - updateConfig({ - valueFieldStorage: { - ...localConfig.valueFieldStorage, - targetTable: undefined, - targetColumn: undefined, - }, - }); - setOpenStorageTableCombo(false); - }} - className="text-xs sm:text-sm" - > - -
- κΈ°λ³Έκ°’ - ν™”λ©΄μ˜ μ—°κ²° ν…Œμ΄λΈ” μ‚¬μš© -
-
- {allTables.map((table) => ( - { - updateConfig({ - valueFieldStorage: { - ...localConfig.valueFieldStorage, - targetTable: table.tableName, - targetColumn: undefined, // ν…Œμ΄λΈ” λ³€κ²½ μ‹œ 컬럼 μ΄ˆκΈ°ν™” - }, - }); - setOpenStorageTableCombo(false); - }} - className="text-xs sm:text-sm" - > - -
- {table.displayName || table.tableName} - {table.displayName && {table.tableName}} -
-
- ))} -
-
-
-
-
-

- 값을 μ €μž₯ν•  ν…Œμ΄λΈ” (κΈ°λ³Έκ°’: ν™”λ©΄ μ—°κ²° ν…Œμ΄λΈ”) -

-
- - {/* μ €μž₯ 컬럼 선택 */} - {localConfig.valueFieldStorage?.targetTable && ( -
- - - - - - - - - - μ»¬λŸΌμ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€. - - {storageTableColumns.map((column) => ( - { - updateConfig({ - valueFieldStorage: { - ...localConfig.valueFieldStorage, - targetColumn: column.columnName, - }, - }); - setOpenStorageColumnCombo(false); - }} - className="text-xs sm:text-sm" - > - -
- {column.displayName || column.columnName} - {column.displayName && {column.columnName}} -
-
- ))} -
-
-
-
-
-

- 값을 μ €μž₯ν•  컬럼λͺ… + {/* μ„€μ • μš”μ•½ */} + {localConfig.tableName && localConfig.targetTable && (localConfig.fieldMappings || []).length > 0 && ( +

+

+ μ„€μ • μš”μ•½ +

+
+

+ μ™ΈλΆ€ ν…Œμ΄λΈ”: {localConfig.tableName} +

+

+ ν‘œμ‹œ ν•„λ“œ: {localConfig.displayField} +

+

+ μ €μž₯ ν…Œμ΄λΈ”: {localConfig.targetTable} +

+

+ λ§€ν•‘ 개수: {(localConfig.fieldMappings || []).length}개

-
- )} - - {/* μ„€λͺ… λ°•μŠ€ */} -
-

- μ €μž₯ μœ„μΉ˜ λ™μž‘ -

-
- {localConfig.valueFieldStorage?.targetTable ? ( - <> -

- μ„ νƒν•œ κ°’({localConfig.valueField})을 -

-

- - {localConfig.valueFieldStorage.targetTable} - {" "} - ν…Œμ΄λΈ”μ˜{" "} - - {localConfig.valueFieldStorage.targetColumn || "(컬럼 λ―Έμ§€μ •)"} - {" "} - μ»¬λŸΌμ— μ €μž₯ν•©λ‹ˆλ‹€. -

- - ) : ( -

κΈ°λ³Έκ°’: ν™”λ©΄μ˜ μ—°κ²° ν…Œμ΄λΈ”μ— μ»΄ν¬λ„ŒνŠΈμ˜ 바인딩 ν•„λ“œλ‘œ μ €μž₯λ©λ‹ˆλ‹€.

- )} -
-
-
- -
-
- - -
-
- {(localConfig.searchFields || []).map((field, index) => ( -
- - -
- ))} -
-
- -
-
- - - updateConfig({ showAdditionalInfo: checked }) - } - /> -
-
- - {localConfig.showAdditionalInfo && ( -
-
- - -
-
- {(localConfig.additionalFields || []).map((field, index) => ( -
- - -
- ))}
)} - - {/* ν•„λ“œ μžλ™ λ§€ν•‘ μ„€μ • */} -
-
-

ν•„λ“œ μžλ™ λ§€ν•‘

-

- μ„ νƒν•œ ν•­λͺ©μ˜ ν•„λ“œλ₯Ό ν™”λ©΄μ˜ λ‹€λ₯Έ μž…λ ₯ ν•„λ“œμ— μžλ™μœΌλ‘œ μ±„μ›Œλ„£μŠ΅λ‹ˆλ‹€ -

-
- -
-
- - - updateConfig({ enableFieldMapping: checked }) - } - /> -
-

- ν™œμ„±ν™”ν•˜λ©΄ ν•­λͺ© 선택 μ‹œ μ„€μ •λœ ν•„λ“œλ“€μ΄ μžλ™μœΌλ‘œ μ±„μ›Œμ§‘λ‹ˆλ‹€ -

-
- - {localConfig.enableFieldMapping && ( -
-
- - -
- -
- {(localConfig.fieldMappings || []).map((mapping, index) => ( -
-
- - λ§€ν•‘ #{index + 1} - - -
- - {/* ν‘œμ‹œλͺ… */} -
- - - updateFieldMapping(index, { label: e.target.value }) - } - placeholder="예: 거래처λͺ…" - className="h-8 text-xs sm:h-10 sm:text-sm" - /> -

- 이 λ§€ν•‘μ˜ μ„€λͺ… (선택사항) -

-
- - {/* μ†ŒμŠ€ ν•„λ“œ (ν…Œμ΄λΈ”μ˜ 컬럼) */} -
- - -

- κ°€μ Έμ˜¬ λ°μ΄ν„°μ˜ 컬럼λͺ… -

-
- - {/* νƒ€κ²Ÿ ν•„λ“œ (ν™”λ©΄μ˜ input ID) */} -
- - - updateFieldMapping(index, { targetField: e.target.value }) - } - placeholder="예: customer_name_input" - className="h-8 text-xs sm:h-10 sm:text-sm" - /> -

- 값을 μ±„μšΈ ν™”λ©΄ μ»΄ν¬λ„ŒνŠΈμ˜ ID (예: input의 id 속성) -

-
- - {/* μ˜ˆμ‹œ μ„€λͺ… */} -
-

- {mapping.sourceField && mapping.targetField ? ( - <> - {mapping.label || "이 ν•„λ“œ"}: ν…Œμ΄λΈ”μ˜{" "} - - {mapping.sourceField} - {" "} - 값을 ν™”λ©΄μ˜{" "} - - {mapping.targetField} - {" "} - μ»΄ν¬λ„ŒνŠΈμ— μžλ™μœΌλ‘œ μ±„μ›λ‹ˆλ‹€ - - ) : ( - "μ†ŒμŠ€ ν•„λ“œμ™€ νƒ€κ²Ÿ ν•„λ“œλ₯Ό λͺ¨λ‘ μ„ νƒν•˜μ„Έμš”" - )} -

-
-
- ))} -
- - {/* μ‚¬μš© μ•ˆλ‚΄ */} - {localConfig.fieldMappings && localConfig.fieldMappings.length > 0 && ( -
-

- μ‚¬μš© 방법 -

-
    -
  • ν™”λ©΄μ—μ„œ 이 검색 μ»΄ν¬λ„ŒνŠΈλ‘œ ν•­λͺ©μ„ μ„ νƒν•˜λ©΄
  • -
  • μ„€μ •λœ 맀핑에 따라 λ‹€λ₯Έ μž…λ ₯ ν•„λ“œλ“€μ΄ μžλ™μœΌλ‘œ μ±„μ›Œμ§‘λ‹ˆλ‹€
  • -
  • νƒ€κ²Ÿ ν•„λ“œ IDλŠ” ν™”λ©΄ λ””μžμ΄λ„ˆμ—μ„œ μ„€μ •ν•œ μ»΄ν¬λ„ŒνŠΈ ID와 μΌμΉ˜ν•΄μ•Ό ν•©λ‹ˆλ‹€
  • -
-
- )} -
- )} -
); } - diff --git a/frontend/lib/registry/components/autocomplete-search-input/types.ts b/frontend/lib/registry/components/autocomplete-search-input/types.ts index 802f27c7..85101e89 100644 --- a/frontend/lib/registry/components/autocomplete-search-input/types.ts +++ b/frontend/lib/registry/components/autocomplete-search-input/types.ts @@ -27,5 +27,7 @@ export interface AutocompleteSearchInputConfig { // ν•„λ“œ μžλ™ λ§€ν•‘ μ„€μ • enableFieldMapping?: boolean; // ν•„λ“œ μžλ™ λ§€ν•‘ ν™œμ„±ν™” μ—¬λΆ€ fieldMappings?: FieldMapping[]; // λ§€ν•‘ν•  ν•„λ“œ λͺ©λ‘ + // μ €μž₯ λŒ€μƒ ν…Œμ΄λΈ” (κ°„μ†Œν™” 버전) + targetTable?: string; }