fix(button-actions): openModalWithData 액션에서 선택된 데이터 전달 누락 수정
- handleOpenModalWithData에서 modalDataStore 데이터를 selectedData/selectedIds로 이벤트에 포함 - RepeatScreenModal에서 groupedData로 사용할 수 있도록 데이터 전달 경로 완성 - ButtonConfigPanel 필드 매핑 UI를 세로 배치로 변경하여 가독성 개선 - split-panel-layout2 컴포넌트 타입 소스 테이블 감지 지원 추가 - currentTableName 폴백 로직 추가로 테이블명 감지 안정성 향상
This commit is contained in:
parent
ffb59b4e1b
commit
f6051e8bbd
|
|
@ -333,22 +333,72 @@ export const ButtonConfigPanel: React.FC<ButtonConfigPanelProps> = ({
|
|||
|
||||
const loadModalMappingColumns = async () => {
|
||||
// 소스 테이블: 현재 화면의 분할 패널 또는 테이블에서 감지
|
||||
// allComponents에서 split-panel-layout 또는 table-list 찾기
|
||||
let sourceTableName: string | null = null;
|
||||
|
||||
console.log("[openModalWithData] 컬럼 로드 시작:", {
|
||||
allComponentsCount: allComponents.length,
|
||||
currentTableName,
|
||||
targetScreenId: config.action?.targetScreenId,
|
||||
});
|
||||
|
||||
// 모든 컴포넌트 타입 로그
|
||||
allComponents.forEach((comp, idx) => {
|
||||
const compType = comp.componentType || (comp as any).componentConfig?.type;
|
||||
console.log(` [${idx}] componentType: ${compType}, tableName: ${(comp as any).componentConfig?.tableName || (comp as any).componentConfig?.leftPanel?.tableName || 'N/A'}`);
|
||||
});
|
||||
|
||||
for (const comp of allComponents) {
|
||||
const compType = comp.componentType || (comp as any).componentConfig?.type;
|
||||
const compConfig = (comp as any).componentConfig || {};
|
||||
|
||||
// 분할 패널 타입들 (다양한 경로에서 테이블명 추출)
|
||||
if (compType === "split-panel-layout" || compType === "screen-split-panel") {
|
||||
// 분할 패널의 좌측 테이블명
|
||||
sourceTableName = (comp as any).componentConfig?.leftPanel?.tableName ||
|
||||
(comp as any).componentConfig?.leftTableName;
|
||||
break;
|
||||
sourceTableName = compConfig?.leftPanel?.tableName ||
|
||||
compConfig?.leftTableName ||
|
||||
compConfig?.tableName;
|
||||
if (sourceTableName) {
|
||||
console.log(`✅ [openModalWithData] split-panel-layout에서 소스 테이블 감지: ${sourceTableName}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// split-panel-layout2 타입 (새로운 분할 패널)
|
||||
if (compType === "split-panel-layout2") {
|
||||
sourceTableName = compConfig?.leftPanel?.tableName ||
|
||||
compConfig?.tableName ||
|
||||
compConfig?.leftTableName;
|
||||
if (sourceTableName) {
|
||||
console.log(`✅ [openModalWithData] split-panel-layout2에서 소스 테이블 감지: ${sourceTableName}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 테이블 리스트 타입
|
||||
if (compType === "table-list") {
|
||||
sourceTableName = (comp as any).componentConfig?.tableName;
|
||||
sourceTableName = compConfig?.tableName;
|
||||
if (sourceTableName) {
|
||||
console.log(`✅ [openModalWithData] table-list에서 소스 테이블 감지: ${sourceTableName}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 🆕 모든 컴포넌트에서 tableName 찾기 (폴백)
|
||||
if (!sourceTableName && compConfig?.tableName) {
|
||||
sourceTableName = compConfig.tableName;
|
||||
console.log(`✅ [openModalWithData] ${compType}에서 소스 테이블 감지 (폴백): ${sourceTableName}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 여전히 없으면 currentTableName 사용 (화면 레벨 테이블명)
|
||||
if (!sourceTableName && currentTableName) {
|
||||
sourceTableName = currentTableName;
|
||||
console.log(`✅ [openModalWithData] currentTableName에서 소스 테이블 사용: ${sourceTableName}`);
|
||||
}
|
||||
|
||||
if (!sourceTableName) {
|
||||
console.warn("[openModalWithData] 소스 테이블을 찾을 수 없습니다.");
|
||||
}
|
||||
|
||||
// 소스 테이블 컬럼 로드
|
||||
if (sourceTableName) {
|
||||
|
|
@ -361,11 +411,11 @@ export const ButtonConfigPanel: React.FC<ButtonConfigPanelProps> = ({
|
|||
|
||||
if (Array.isArray(columnData)) {
|
||||
const columns = columnData.map((col: any) => ({
|
||||
name: col.name || col.columnName,
|
||||
label: col.displayName || col.label || col.columnLabel || col.name || col.columnName,
|
||||
name: col.name || col.columnName || col.column_name,
|
||||
label: col.displayName || col.label || col.columnLabel || col.display_name || col.name || col.columnName || col.column_name,
|
||||
}));
|
||||
setModalSourceColumns(columns);
|
||||
console.log(`✅ [openModalWithData] 소스 테이블(${sourceTableName}) 컬럼 로드:`, columns.length);
|
||||
console.log(`✅ [openModalWithData] 소스 테이블(${sourceTableName}) 컬럼 로드 완료:`, columns.length);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
@ -379,8 +429,12 @@ export const ButtonConfigPanel: React.FC<ButtonConfigPanelProps> = ({
|
|||
try {
|
||||
// 타겟 화면 정보 가져오기
|
||||
const screenResponse = await apiClient.get(`/screen-management/screens/${targetScreenId}`);
|
||||
console.log("[openModalWithData] 타겟 화면 응답:", screenResponse.data);
|
||||
|
||||
if (screenResponse.data.success && screenResponse.data.data) {
|
||||
const targetTableName = screenResponse.data.data.tableName;
|
||||
console.log("[openModalWithData] 타겟 화면 테이블명:", targetTableName);
|
||||
|
||||
if (targetTableName) {
|
||||
const columnResponse = await apiClient.get(`/table-management/tables/${targetTableName}/columns`);
|
||||
if (columnResponse.data.success) {
|
||||
|
|
@ -390,23 +444,27 @@ export const ButtonConfigPanel: React.FC<ButtonConfigPanelProps> = ({
|
|||
|
||||
if (Array.isArray(columnData)) {
|
||||
const columns = columnData.map((col: any) => ({
|
||||
name: col.name || col.columnName,
|
||||
label: col.displayName || col.label || col.columnLabel || col.name || col.columnName,
|
||||
name: col.name || col.columnName || col.column_name,
|
||||
label: col.displayName || col.label || col.columnLabel || col.display_name || col.name || col.columnName || col.column_name,
|
||||
}));
|
||||
setModalTargetColumns(columns);
|
||||
console.log(`✅ [openModalWithData] 타겟 테이블(${targetTableName}) 컬럼 로드:`, columns.length);
|
||||
console.log(`✅ [openModalWithData] 타겟 테이블(${targetTableName}) 컬럼 로드 완료:`, columns.length);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.warn("[openModalWithData] 타겟 화면에 테이블명이 없습니다.");
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("타겟 화면 테이블 컬럼 로드 실패:", error);
|
||||
}
|
||||
} else {
|
||||
console.warn("[openModalWithData] 타겟 화면 ID가 없습니다.");
|
||||
}
|
||||
};
|
||||
|
||||
loadModalMappingColumns();
|
||||
}, [config.action?.type, config.action?.targetScreenId, allComponents]);
|
||||
}, [config.action?.type, config.action?.targetScreenId, allComponents, currentTableName]);
|
||||
|
||||
// 화면 목록 가져오기 (현재 편집 중인 화면의 회사 코드 기준)
|
||||
useEffect(() => {
|
||||
|
|
@ -1158,11 +1216,12 @@ export const ButtonConfigPanel: React.FC<ButtonConfigPanelProps> = ({
|
|||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
<div className="space-y-3">
|
||||
{(config.action?.fieldMappings || []).map((mapping: any, index: number) => (
|
||||
<div key={index} className="flex items-center gap-2 rounded-md border bg-background p-2">
|
||||
{/* 소스 필드 선택 (Combobox) */}
|
||||
<div className="flex-1">
|
||||
<div key={index} className="rounded-md border bg-background p-3 space-y-2">
|
||||
{/* 소스 필드 선택 (Combobox) - 세로 배치 */}
|
||||
<div className="space-y-1">
|
||||
<Label className="text-[10px] text-muted-foreground">소스 컬럼</Label>
|
||||
<Popover
|
||||
open={modalSourcePopoverOpen[index] || false}
|
||||
onOpenChange={(open) => setModalSourcePopoverOpen((prev) => ({ ...prev, [index]: open }))}
|
||||
|
|
@ -1171,15 +1230,17 @@ export const ButtonConfigPanel: React.FC<ButtonConfigPanelProps> = ({
|
|||
<Button
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
className="h-7 w-full justify-between text-xs"
|
||||
className="h-8 w-full justify-between text-xs"
|
||||
>
|
||||
{mapping.sourceField
|
||||
? modalSourceColumns.find((c) => c.name === mapping.sourceField)?.label || mapping.sourceField
|
||||
: "소스 컬럼 선택"}
|
||||
<span className="truncate">
|
||||
{mapping.sourceField
|
||||
? modalSourceColumns.find((c) => c.name === mapping.sourceField)?.label || mapping.sourceField
|
||||
: "소스 컬럼 선택"}
|
||||
</span>
|
||||
<ChevronsUpDown className="ml-1 h-3 w-3 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-[200px] p-0" align="start">
|
||||
<PopoverContent className="w-[--radix-popover-trigger-width] max-w-[280px] p-0" align="start">
|
||||
<Command>
|
||||
<CommandInput
|
||||
placeholder="컬럼 검색..."
|
||||
|
|
@ -1187,7 +1248,7 @@ export const ButtonConfigPanel: React.FC<ButtonConfigPanelProps> = ({
|
|||
value={modalSourceSearch[index] || ""}
|
||||
onValueChange={(value) => setModalSourceSearch((prev) => ({ ...prev, [index]: value }))}
|
||||
/>
|
||||
<CommandList>
|
||||
<CommandList className="max-h-[200px]">
|
||||
<CommandEmpty className="py-2 text-center text-xs">컬럼을 찾을 수 없습니다</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{modalSourceColumns.map((col) => (
|
||||
|
|
@ -1208,9 +1269,9 @@ export const ButtonConfigPanel: React.FC<ButtonConfigPanelProps> = ({
|
|||
mapping.sourceField === col.name ? "opacity-100" : "opacity-0"
|
||||
)}
|
||||
/>
|
||||
<span>{col.label}</span>
|
||||
<span className="truncate">{col.label}</span>
|
||||
{col.label !== col.name && (
|
||||
<span className="ml-1 text-muted-foreground">({col.name})</span>
|
||||
<span className="ml-1 text-muted-foreground truncate">({col.name})</span>
|
||||
)}
|
||||
</CommandItem>
|
||||
))}
|
||||
|
|
@ -1221,10 +1282,14 @@ export const ButtonConfigPanel: React.FC<ButtonConfigPanelProps> = ({
|
|||
</Popover>
|
||||
</div>
|
||||
|
||||
<span className="text-xs text-muted-foreground">→</span>
|
||||
{/* 화살표 표시 */}
|
||||
<div className="flex justify-center">
|
||||
<span className="text-xs text-muted-foreground">↓</span>
|
||||
</div>
|
||||
|
||||
{/* 타겟 필드 선택 (Combobox) */}
|
||||
<div className="flex-1">
|
||||
{/* 타겟 필드 선택 (Combobox) - 세로 배치 */}
|
||||
<div className="space-y-1">
|
||||
<Label className="text-[10px] text-muted-foreground">타겟 컬럼</Label>
|
||||
<Popover
|
||||
open={modalTargetPopoverOpen[index] || false}
|
||||
onOpenChange={(open) => setModalTargetPopoverOpen((prev) => ({ ...prev, [index]: open }))}
|
||||
|
|
@ -1233,15 +1298,17 @@ export const ButtonConfigPanel: React.FC<ButtonConfigPanelProps> = ({
|
|||
<Button
|
||||
variant="outline"
|
||||
role="combobox"
|
||||
className="h-7 w-full justify-between text-xs"
|
||||
className="h-8 w-full justify-between text-xs"
|
||||
>
|
||||
{mapping.targetField
|
||||
? modalTargetColumns.find((c) => c.name === mapping.targetField)?.label || mapping.targetField
|
||||
: "타겟 컬럼 선택"}
|
||||
<span className="truncate">
|
||||
{mapping.targetField
|
||||
? modalTargetColumns.find((c) => c.name === mapping.targetField)?.label || mapping.targetField
|
||||
: "타겟 컬럼 선택"}
|
||||
</span>
|
||||
<ChevronsUpDown className="ml-1 h-3 w-3 shrink-0 opacity-50" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-[200px] p-0" align="start">
|
||||
<PopoverContent className="w-[--radix-popover-trigger-width] max-w-[280px] p-0" align="start">
|
||||
<Command>
|
||||
<CommandInput
|
||||
placeholder="컬럼 검색..."
|
||||
|
|
@ -1249,7 +1316,7 @@ export const ButtonConfigPanel: React.FC<ButtonConfigPanelProps> = ({
|
|||
value={modalTargetSearch[index] || ""}
|
||||
onValueChange={(value) => setModalTargetSearch((prev) => ({ ...prev, [index]: value }))}
|
||||
/>
|
||||
<CommandList>
|
||||
<CommandList className="max-h-[200px]">
|
||||
<CommandEmpty className="py-2 text-center text-xs">컬럼을 찾을 수 없습니다</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{modalTargetColumns.map((col) => (
|
||||
|
|
@ -1270,9 +1337,9 @@ export const ButtonConfigPanel: React.FC<ButtonConfigPanelProps> = ({
|
|||
mapping.targetField === col.name ? "opacity-100" : "opacity-0"
|
||||
)}
|
||||
/>
|
||||
<span>{col.label}</span>
|
||||
<span className="truncate">{col.label}</span>
|
||||
{col.label !== col.name && (
|
||||
<span className="ml-1 text-muted-foreground">({col.name})</span>
|
||||
<span className="ml-1 text-muted-foreground truncate">({col.name})</span>
|
||||
)}
|
||||
</CommandItem>
|
||||
))}
|
||||
|
|
@ -1284,19 +1351,22 @@ export const ButtonConfigPanel: React.FC<ButtonConfigPanelProps> = ({
|
|||
</div>
|
||||
|
||||
{/* 삭제 버튼 */}
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="h-7 w-7 text-destructive hover:bg-destructive/10"
|
||||
onClick={() => {
|
||||
const mappings = [...(config.action?.fieldMappings || [])];
|
||||
mappings.splice(index, 1);
|
||||
onUpdateProperty("componentConfig.action.fieldMappings", mappings);
|
||||
}}
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</Button>
|
||||
<div className="flex justify-end pt-1">
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-6 text-[10px] text-destructive hover:bg-destructive/10"
|
||||
onClick={() => {
|
||||
const mappings = [...(config.action?.fieldMappings || [])];
|
||||
mappings.splice(index, 1);
|
||||
onUpdateProperty("componentConfig.action.fieldMappings", mappings);
|
||||
}}
|
||||
>
|
||||
<X className="h-3 w-3 mr-1" />
|
||||
삭제
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -2020,11 +2020,18 @@ export class ButtonActionExecutor {
|
|||
});
|
||||
}
|
||||
|
||||
// 🆕 modalDataStore에서 선택된 전체 데이터 가져오기 (RepeatScreenModal에서 사용)
|
||||
const modalData = dataRegistry[dataSourceId] || [];
|
||||
const selectedData = modalData.map((item: any) => item.originalData || item);
|
||||
const selectedIds = selectedData.map((row: any) => row.id).filter(Boolean);
|
||||
|
||||
console.log("📦 [openModalWithData] 부모 데이터 전달:", {
|
||||
dataSourceId,
|
||||
rawParentData,
|
||||
mappedParentData: parentData,
|
||||
fieldMappings: config.fieldMappings,
|
||||
selectedDataCount: selectedData.length,
|
||||
selectedIds,
|
||||
});
|
||||
|
||||
// 🆕 전역 모달 상태 업데이트를 위한 이벤트 발생 (URL 파라미터 포함)
|
||||
|
|
@ -2036,6 +2043,9 @@ export class ButtonActionExecutor {
|
|||
size: config.modalSize || "lg", // 데이터 입력 화면은 기본 large
|
||||
urlParams: { dataSourceId }, // 🆕 주 데이터 소스만 전달 (나머지는 modalDataStore에서 자동으로 찾음)
|
||||
splitPanelParentData: parentData, // 🆕 부모 데이터 전달 (excludeFilter에서 사용)
|
||||
// 🆕 선택된 데이터 전달 (RepeatScreenModal에서 groupedData로 사용)
|
||||
selectedData: selectedData,
|
||||
selectedIds: selectedIds,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue