diff --git a/frontend/lib/registry/components/divider-line/DividerLineComponent.tsx b/frontend/lib/registry/components/divider-line/DividerLineComponent.tsx index 5cc4fcfd..d2b61c90 100644 --- a/frontend/lib/registry/components/divider-line/DividerLineComponent.tsx +++ b/frontend/lib/registry/components/divider-line/DividerLineComponent.tsx @@ -53,6 +53,7 @@ export const DividerLineComponent: React.FC = ({ }; // DOM에 전달하면 안 되는 React-specific props 필터링 + // eslint-disable-next-line @typescript-eslint/no-unused-vars const { selectedScreen, onZoneComponentDrop, @@ -70,8 +71,40 @@ export const DividerLineComponent: React.FC = ({ tableName: _tableName, onRefresh: _onRefresh, onClose: _onClose, + // 추가된 props 필터링 + webType: _webType, + autoGeneration: _autoGeneration, + isInteractive: _isInteractive, + formData: _formData, + onFormDataChange: _onFormDataChange, + menuId: _menuId, + menuObjid: _menuObjid, + onSave: _onSave, + userId: _userId, + userName: _userName, + companyCode: _companyCode, + isInModal: _isInModal, + readonly: _readonly, + originalData: _originalData, + allComponents: _allComponents, + onUpdateLayout: _onUpdateLayout, + selectedRows: _selectedRows, + selectedRowsData: _selectedRowsData, + onSelectedRowsChange: _onSelectedRowsChange, + sortBy: _sortBy, + sortOrder: _sortOrder, + tableDisplayData: _tableDisplayData, + flowSelectedData: _flowSelectedData, + flowSelectedStepId: _flowSelectedStepId, + onFlowSelectedDataChange: _onFlowSelectedDataChange, + onConfigChange: _onConfigChange, + refreshKey: _refreshKey, + flowRefreshKey: _flowRefreshKey, + onFlowRefresh: _onFlowRefresh, + isPreview: _isPreview, + groupedData: _groupedData, ...domProps - } = props; + } = props as any; return (
diff --git a/frontend/lib/registry/components/location-swap-selector/LocationSwapSelectorComponent.tsx b/frontend/lib/registry/components/location-swap-selector/LocationSwapSelectorComponent.tsx index 732deb8b..02f4e436 100644 --- a/frontend/lib/registry/components/location-swap-selector/LocationSwapSelectorComponent.tsx +++ b/frontend/lib/registry/components/location-swap-selector/LocationSwapSelectorComponent.tsx @@ -103,11 +103,36 @@ export function LocationSwapSelectorComponent(props: LocationSwapSelectorProps) const departureValue = formData[departureField] || ""; const destinationValue = formData[destinationField] || ""; + // 기본 옵션 (포항/광양) + const DEFAULT_OPTIONS: LocationOption[] = [ + { value: "pohang", label: "포항" }, + { value: "gwangyang", label: "광양" }, + ]; + // 옵션 로드 useEffect(() => { const loadOptions = async () => { - if (dataSource.type === "static") { - setOptions(dataSource.staticOptions || []); + console.log("[LocationSwapSelector] 옵션 로드 시작:", { dataSource, isDesignMode }); + + // 정적 옵션 처리 (기본값) + // type이 없거나 static이거나, table인데 tableName이 없는 경우 + const shouldUseStatic = + !dataSource.type || + dataSource.type === "static" || + (dataSource.type === "table" && !dataSource.tableName) || + (dataSource.type === "code" && !dataSource.codeCategory); + + if (shouldUseStatic) { + const staticOpts = dataSource.staticOptions || []; + // 정적 옵션이 설정되어 있으면 사용 + if (staticOpts.length > 0 && staticOpts[0]?.value) { + console.log("[LocationSwapSelector] 정적 옵션 사용:", staticOpts); + setOptions(staticOpts); + } else { + // 기본값 (포항/광양) + console.log("[LocationSwapSelector] 기본 옵션 사용:", DEFAULT_OPTIONS); + setOptions(DEFAULT_OPTIONS); + } return; } @@ -159,17 +184,7 @@ export function LocationSwapSelectorComponent(props: LocationSwapSelectorProps) } }; - if (!isDesignMode) { - loadOptions(); - } else { - // 디자인 모드에서는 샘플 데이터 - setOptions([ - { value: "seoul", label: "서울" }, - { value: "busan", label: "부산" }, - { value: "pohang", label: "포항" }, - { value: "gwangyang", label: "광양" }, - ]); - } + loadOptions(); }, [dataSource, isDesignMode]); // 출발지 변경 @@ -250,7 +265,7 @@ export function LocationSwapSelectorComponent(props: LocationSwapSelectorProps)
@@ -276,7 +295,6 @@ export function LocationSwapSelectorComponent(props: LocationSwapSelectorProps) variant="ghost" size="icon" onClick={handleSwap} - disabled={isDesignMode || !departureValue || !destinationValue} className={cn( "mx-2 h-10 w-10 rounded-full border bg-background transition-transform hover:bg-muted", isSwapping && "rotate-180" @@ -292,7 +310,7 @@ export function LocationSwapSelectorComponent(props: LocationSwapSelectorProps) @@ -328,17 +350,21 @@ export function LocationSwapSelectorComponent(props: LocationSwapSelectorProps) @@ -349,7 +375,6 @@ export function LocationSwapSelectorComponent(props: LocationSwapSelectorProps) variant="outline" size="icon" onClick={handleSwap} - disabled={isDesignMode} className="mt-5 h-10 w-10" > @@ -361,17 +386,21 @@ export function LocationSwapSelectorComponent(props: LocationSwapSelectorProps) @@ -389,17 +418,21 @@ export function LocationSwapSelectorComponent(props: LocationSwapSelectorProps) @@ -409,7 +442,6 @@ export function LocationSwapSelectorComponent(props: LocationSwapSelectorProps) variant="ghost" size="sm" onClick={handleSwap} - disabled={isDesignMode} className="h-8 w-8 p-0" > @@ -419,17 +451,21 @@ export function LocationSwapSelectorComponent(props: LocationSwapSelectorProps) diff --git a/frontend/lib/registry/components/location-swap-selector/LocationSwapSelectorConfigPanel.tsx b/frontend/lib/registry/components/location-swap-selector/LocationSwapSelectorConfigPanel.tsx index c18f6514..518b6172 100644 --- a/frontend/lib/registry/components/location-swap-selector/LocationSwapSelectorConfigPanel.tsx +++ b/frontend/lib/registry/components/location-swap-selector/LocationSwapSelectorConfigPanel.tsx @@ -139,13 +139,83 @@ export function LocationSwapSelectorConfigPanel({ - 정적 옵션 (하드코딩) - 테이블 - 코드 관리 + 고정 옵션 (포항/광양 등) + 테이블에서 가져오기 + 코드 관리에서 가져오기 + {/* 고정 옵션 설정 (type이 static일 때) */} + {(!config?.dataSource?.type || config?.dataSource?.type === "static") && ( +
+

고정 옵션 설정

+
+
+ + { + const options = config?.dataSource?.staticOptions || []; + const newOptions = [...options]; + newOptions[0] = { ...newOptions[0], value: e.target.value }; + handleChange("dataSource.staticOptions", newOptions); + }} + placeholder="예: pohang" + className="h-8 text-xs" + /> +
+
+ + { + const options = config?.dataSource?.staticOptions || []; + const newOptions = [...options]; + newOptions[0] = { ...newOptions[0], label: e.target.value }; + handleChange("dataSource.staticOptions", newOptions); + }} + placeholder="예: 포항" + className="h-8 text-xs" + /> +
+
+
+
+ + { + const options = config?.dataSource?.staticOptions || []; + const newOptions = [...options]; + newOptions[1] = { ...newOptions[1], value: e.target.value }; + handleChange("dataSource.staticOptions", newOptions); + }} + placeholder="예: gwangyang" + className="h-8 text-xs" + /> +
+
+ + { + const options = config?.dataSource?.staticOptions || []; + const newOptions = [...options]; + newOptions[1] = { ...newOptions[1], label: e.target.value }; + handleChange("dataSource.staticOptions", newOptions); + }} + placeholder="예: 광양" + className="h-8 text-xs" + /> +
+
+

+ 고정된 2개 장소만 사용할 때 설정하세요. (예: 포항 ↔ 광양) +

+
+ )} + {/* 테이블 선택 (type이 table일 때) */} {config?.dataSource?.type === "table" && ( <> diff --git a/frontend/lib/registry/components/location-swap-selector/LocationSwapSelectorRenderer.tsx b/frontend/lib/registry/components/location-swap-selector/LocationSwapSelectorRenderer.tsx index 8e3fe5f7..6adc4724 100644 --- a/frontend/lib/registry/components/location-swap-selector/LocationSwapSelectorRenderer.tsx +++ b/frontend/lib/registry/components/location-swap-selector/LocationSwapSelectorRenderer.tsx @@ -12,7 +12,28 @@ export class LocationSwapSelectorRenderer extends AutoRegisteringComponentRender static componentDefinition = LocationSwapSelectorDefinition; render(): React.ReactElement { - return ; + const { component, formData, onFormDataChange, isDesignMode, style, ...restProps } = this.props; + + // component.componentConfig에서 설정 가져오기 + const componentConfig = component?.componentConfig || {}; + + console.log("[LocationSwapSelectorRenderer] render:", { + componentConfig, + formData, + isDesignMode + }); + + return ( + + ); } } diff --git a/frontend/lib/registry/components/location-swap-selector/index.ts b/frontend/lib/registry/components/location-swap-selector/index.ts index 60b62008..c4c30418 100644 --- a/frontend/lib/registry/components/location-swap-selector/index.ts +++ b/frontend/lib/registry/components/location-swap-selector/index.ts @@ -20,12 +20,15 @@ export const LocationSwapSelectorDefinition = createComponentDefinition({ defaultConfig: { // 데이터 소스 설정 dataSource: { - type: "table", // "table" | "code" | "static" + type: "static", // "table" | "code" | "static" tableName: "", // 장소 테이블명 valueField: "location_code", // 값 필드 labelField: "location_name", // 표시 필드 codeCategory: "", // 코드 관리 카테고리 (type이 "code"일 때) - staticOptions: [], // 정적 옵션 (type이 "static"일 때) + staticOptions: [ + { value: "pohang", label: "포항" }, + { value: "gwangyang", label: "광양" }, + ], // 정적 옵션 (type이 "static"일 때) }, // 필드 매핑 departureField: "departure", // 출발지 저장 필드 diff --git a/frontend/lib/utils/buttonActions.ts b/frontend/lib/utils/buttonActions.ts index cf53a490..537a6e7d 100644 --- a/frontend/lib/utils/buttonActions.ts +++ b/frontend/lib/utils/buttonActions.ts @@ -271,6 +271,9 @@ export class ButtonActionExecutor { case "geolocation": return await this.handleGeolocation(config, context); + case "swap_fields": + return await this.handleSwapFields(config, context); + case "update_field": return await this.handleUpdateField(config, context); @@ -3412,6 +3415,59 @@ export class ButtonActionExecutor { } } + /** + * 필드 값 교환 액션 처리 (예: 출발지 ↔ 도착지) + */ + private static async handleSwapFields(config: ButtonActionConfig, context: ButtonActionContext): Promise { + try { + console.log("🔄 필드 값 교환 액션 실행:", { config, context }); + + const { formData, onFormDataChange } = context; + + // 교환할 필드 확인 + const fieldA = config.swapFieldA; + const fieldB = config.swapFieldB; + + if (!fieldA || !fieldB) { + toast.error("교환할 필드가 설정되지 않았습니다."); + return false; + } + + // 현재 값 가져오기 + const valueA = formData?.[fieldA]; + const valueB = formData?.[fieldB]; + + console.log("🔄 교환 전:", { [fieldA]: valueA, [fieldB]: valueB }); + + // 값 교환 + if (onFormDataChange) { + onFormDataChange(fieldA, valueB); + onFormDataChange(fieldB, valueA); + } + + // 관련 필드도 함께 교환 (예: 위도/경도) + if (config.swapRelatedFields && config.swapRelatedFields.length > 0) { + for (const related of config.swapRelatedFields) { + const relatedValueA = formData?.[related.fieldA]; + const relatedValueB = formData?.[related.fieldB]; + if (onFormDataChange) { + onFormDataChange(related.fieldA, relatedValueB); + onFormDataChange(related.fieldB, relatedValueA); + } + } + } + + console.log("🔄 교환 후:", { [fieldA]: valueB, [fieldB]: valueA }); + + toast.success(config.successMessage || "값이 교환되었습니다."); + return true; + } catch (error) { + console.error("❌ 필드 값 교환 오류:", error); + toast.error(config.errorMessage || "값 교환 중 오류가 발생했습니다."); + return false; + } + } + /** * 필드 값 변경 액션 처리 (예: status를 active로 변경) */