feature/v2-unified-renewal #379
|
|
@ -101,6 +101,17 @@ export const ButtonConfigPanel: React.FC<ButtonConfigPanelProps> = ({
|
||||||
const [modalSourceSearch, setModalSourceSearch] = useState<Record<number, string>>({});
|
const [modalSourceSearch, setModalSourceSearch] = useState<Record<number, string>>({});
|
||||||
const [modalTargetSearch, setModalTargetSearch] = useState<Record<number, string>>({});
|
const [modalTargetSearch, setModalTargetSearch] = useState<Record<number, string>>({});
|
||||||
|
|
||||||
|
// 🆕 modal 액션용 필드 매핑 상태
|
||||||
|
const [modalActionSourceTable, setModalActionSourceTable] = useState<string | null>(null);
|
||||||
|
const [modalActionTargetTable, setModalActionTargetTable] = useState<string | null>(null);
|
||||||
|
const [modalActionSourceColumns, setModalActionSourceColumns] = useState<Array<{ name: string; label: string }>>([]);
|
||||||
|
const [modalActionTargetColumns, setModalActionTargetColumns] = useState<Array<{ name: string; label: string }>>([]);
|
||||||
|
const [modalActionFieldMappings, setModalActionFieldMappings] = useState<Array<{ sourceField: string; targetField: string }>>([]);
|
||||||
|
const [modalFieldMappingSourceOpen, setModalFieldMappingSourceOpen] = useState<Record<number, boolean>>({});
|
||||||
|
const [modalFieldMappingTargetOpen, setModalFieldMappingTargetOpen] = useState<Record<number, boolean>>({});
|
||||||
|
const [modalFieldMappingSourceSearch, setModalFieldMappingSourceSearch] = useState<Record<number, string>>({});
|
||||||
|
const [modalFieldMappingTargetSearch, setModalFieldMappingTargetSearch] = useState<Record<number, string>>({});
|
||||||
|
|
||||||
// 🎯 플로우 위젯이 화면에 있는지 확인
|
// 🎯 플로우 위젯이 화면에 있는지 확인
|
||||||
const hasFlowWidget = useMemo(() => {
|
const hasFlowWidget = useMemo(() => {
|
||||||
const found = allComponents.some((comp: any) => {
|
const found = allComponents.some((comp: any) => {
|
||||||
|
|
@ -328,6 +339,123 @@ export const ButtonConfigPanel: React.FC<ButtonConfigPanelProps> = ({
|
||||||
loadColumns();
|
loadColumns();
|
||||||
}, [config.action?.dataTransfer?.sourceTable, config.action?.dataTransfer?.targetTable]);
|
}, [config.action?.dataTransfer?.sourceTable, config.action?.dataTransfer?.targetTable]);
|
||||||
|
|
||||||
|
// 🆕 modal 액션: 대상 화면 테이블 조회 및 필드 매핑 로드
|
||||||
|
useEffect(() => {
|
||||||
|
const actionType = config.action?.type;
|
||||||
|
if (actionType !== "modal") return;
|
||||||
|
|
||||||
|
const autoDetect = config.action?.autoDetectDataSource;
|
||||||
|
if (!autoDetect) {
|
||||||
|
// 데이터 전달이 비활성화되면 상태 초기화
|
||||||
|
setModalActionSourceTable(null);
|
||||||
|
setModalActionTargetTable(null);
|
||||||
|
setModalActionSourceColumns([]);
|
||||||
|
setModalActionTargetColumns([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetScreenId = config.action?.targetScreenId;
|
||||||
|
if (!targetScreenId) return;
|
||||||
|
|
||||||
|
const loadModalActionMappingData = async () => {
|
||||||
|
// 1. 소스 테이블 감지 (현재 화면)
|
||||||
|
let sourceTableName: string | null = currentTableName || null;
|
||||||
|
|
||||||
|
// allComponents에서 분할패널/테이블리스트/통합목록 감지
|
||||||
|
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 = compConfig.leftPanel?.tableName || compConfig.tableName || null;
|
||||||
|
if (sourceTableName) break;
|
||||||
|
}
|
||||||
|
if (compType === "table-list") {
|
||||||
|
sourceTableName = compConfig.tableName || compConfig.selectedTable || null;
|
||||||
|
if (sourceTableName) break;
|
||||||
|
}
|
||||||
|
if (compType === "unified-list") {
|
||||||
|
sourceTableName = compConfig.dataSource?.table || compConfig.tableName || null;
|
||||||
|
if (sourceTableName) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setModalActionSourceTable(sourceTableName);
|
||||||
|
|
||||||
|
// 2. 대상 화면의 테이블 조회
|
||||||
|
let targetTableName: string | null = null;
|
||||||
|
try {
|
||||||
|
const screenResponse = await apiClient.get(`/screen-management/screens/${targetScreenId}`);
|
||||||
|
if (screenResponse.data.success && screenResponse.data.data) {
|
||||||
|
targetTableName = screenResponse.data.data.tableName || null;
|
||||||
|
} else if (screenResponse.data?.tableName) {
|
||||||
|
// 직접 데이터 반환 형식인 경우
|
||||||
|
targetTableName = screenResponse.data.tableName || null;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("대상 화면 정보 로드 실패:", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
setModalActionTargetTable(targetTableName);
|
||||||
|
|
||||||
|
// 3. 소스 테이블 컬럼 로드
|
||||||
|
if (sourceTableName) {
|
||||||
|
try {
|
||||||
|
const response = await apiClient.get(`/table-management/tables/${sourceTableName}/columns`);
|
||||||
|
if (response.data.success) {
|
||||||
|
let columnData = response.data.data;
|
||||||
|
if (!Array.isArray(columnData) && columnData?.columns) columnData = columnData.columns;
|
||||||
|
if (!Array.isArray(columnData) && columnData?.data) columnData = columnData.data;
|
||||||
|
|
||||||
|
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,
|
||||||
|
}));
|
||||||
|
setModalActionSourceColumns(columns);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("소스 테이블 컬럼 로드 실패:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 대상 테이블 컬럼 로드
|
||||||
|
if (targetTableName) {
|
||||||
|
try {
|
||||||
|
const response = await apiClient.get(`/table-management/tables/${targetTableName}/columns`);
|
||||||
|
if (response.data.success) {
|
||||||
|
let columnData = response.data.data;
|
||||||
|
if (!Array.isArray(columnData) && columnData?.columns) columnData = columnData.columns;
|
||||||
|
if (!Array.isArray(columnData) && columnData?.data) columnData = columnData.data;
|
||||||
|
|
||||||
|
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,
|
||||||
|
}));
|
||||||
|
setModalActionTargetColumns(columns);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("대상 테이블 컬럼 로드 실패:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 기존 필드 매핑 로드 또는 자동 매핑 생성
|
||||||
|
const existingMappings = config.action?.fieldMappings || [];
|
||||||
|
if (existingMappings.length > 0) {
|
||||||
|
setModalActionFieldMappings(existingMappings);
|
||||||
|
} else if (sourceTableName && targetTableName && sourceTableName === targetTableName) {
|
||||||
|
// 테이블이 같으면 자동 매핑 (동일 컬럼명)
|
||||||
|
setModalActionFieldMappings([]); // 빈 배열 = 자동 매핑
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
loadModalActionMappingData();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [config.action?.type, config.action?.autoDetectDataSource, config.action?.targetScreenId, currentTableName, allComponents]);
|
||||||
|
|
||||||
// 🆕 openModalWithData 소스/타겟 테이블 컬럼 로드
|
// 🆕 openModalWithData 소스/타겟 테이블 컬럼 로드
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const actionType = config.action?.type;
|
const actionType = config.action?.type;
|
||||||
|
|
@ -791,6 +919,10 @@ export const ButtonConfigPanel: React.FC<ButtonConfigPanelProps> = ({
|
||||||
checked={component.componentConfig?.action?.autoDetectDataSource === true}
|
checked={component.componentConfig?.action?.autoDetectDataSource === true}
|
||||||
onCheckedChange={(checked) => {
|
onCheckedChange={(checked) => {
|
||||||
onUpdateProperty("componentConfig.action.autoDetectDataSource", checked);
|
onUpdateProperty("componentConfig.action.autoDetectDataSource", checked);
|
||||||
|
if (!checked) {
|
||||||
|
// 체크 해제 시 필드 매핑도 초기화
|
||||||
|
onUpdateProperty("componentConfig.action.fieldMappings", []);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
|
|
@ -802,6 +934,192 @@ export const ButtonConfigPanel: React.FC<ButtonConfigPanelProps> = ({
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* 🆕 필드 매핑 UI (데이터 전달 활성화 + 테이블이 다른 경우) */}
|
||||||
|
{component.componentConfig?.action?.autoDetectDataSource === true && (
|
||||||
|
<div className="mt-4 space-y-3 rounded-lg border bg-background p-3">
|
||||||
|
{/* 테이블 정보 표시 */}
|
||||||
|
<div className="flex items-center justify-between text-xs">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Database className="h-3 w-3 text-muted-foreground" />
|
||||||
|
<span className="text-muted-foreground">소스:</span>
|
||||||
|
<span className="font-medium">{modalActionSourceTable || "감지 중..."}</span>
|
||||||
|
</div>
|
||||||
|
<span className="text-muted-foreground">→</span>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="text-muted-foreground">대상:</span>
|
||||||
|
<span className="font-medium">{modalActionTargetTable || "감지 중..."}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 테이블이 같으면 자동 매핑 안내 */}
|
||||||
|
{modalActionSourceTable && modalActionTargetTable && modalActionSourceTable === modalActionTargetTable && (
|
||||||
|
<div className="rounded-md bg-green-50 p-2 text-xs text-green-700 dark:bg-green-950/30 dark:text-green-400">
|
||||||
|
동일한 테이블입니다. 컬럼명이 같은 필드는 자동으로 매핑됩니다.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 테이블이 다르면 필드 매핑 UI 표시 */}
|
||||||
|
{modalActionSourceTable && modalActionTargetTable && modalActionSourceTable !== modalActionTargetTable && (
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<Label className="text-xs font-medium">필드 매핑</Label>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
className="h-6 text-xs"
|
||||||
|
onClick={() => {
|
||||||
|
const newMappings = [...(component.componentConfig?.action?.fieldMappings || []), { sourceField: "", targetField: "" }];
|
||||||
|
setModalActionFieldMappings(newMappings);
|
||||||
|
onUpdateProperty("componentConfig.action.fieldMappings", newMappings);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Plus className="mr-1 h-3 w-3" />
|
||||||
|
매핑 추가
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{(component.componentConfig?.action?.fieldMappings || []).length === 0 && (
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
컬럼명이 다른 경우 매핑을 추가하세요. 매핑이 없으면 동일 컬럼명만 전달됩니다.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{(component.componentConfig?.action?.fieldMappings || []).map((mapping: any, index: number) => (
|
||||||
|
<div key={index} className="flex items-center gap-2">
|
||||||
|
{/* 소스 필드 선택 */}
|
||||||
|
<Popover
|
||||||
|
open={modalFieldMappingSourceOpen[index] || false}
|
||||||
|
onOpenChange={(open) => setModalFieldMappingSourceOpen((prev) => ({ ...prev, [index]: open }))}
|
||||||
|
>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<Button variant="outline" size="sm" className="h-7 flex-1 justify-between text-xs">
|
||||||
|
{mapping.sourceField
|
||||||
|
? modalActionSourceColumns.find((c) => c.name === mapping.sourceField)?.label || mapping.sourceField
|
||||||
|
: "소스 컬럼 선택"}
|
||||||
|
<ChevronsUpDown className="ml-1 h-3 w-3 opacity-50" />
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-[200px] p-0" align="start">
|
||||||
|
<Command>
|
||||||
|
<CommandInput
|
||||||
|
placeholder="컬럼 검색..."
|
||||||
|
value={modalFieldMappingSourceSearch[index] || ""}
|
||||||
|
onValueChange={(val) => setModalFieldMappingSourceSearch((prev) => ({ ...prev, [index]: val }))}
|
||||||
|
/>
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>컬럼을 찾을 수 없습니다.</CommandEmpty>
|
||||||
|
<CommandGroup>
|
||||||
|
{modalActionSourceColumns
|
||||||
|
.filter((col) =>
|
||||||
|
col.name.toLowerCase().includes((modalFieldMappingSourceSearch[index] || "").toLowerCase()) ||
|
||||||
|
col.label.toLowerCase().includes((modalFieldMappingSourceSearch[index] || "").toLowerCase())
|
||||||
|
)
|
||||||
|
.map((col) => (
|
||||||
|
<CommandItem
|
||||||
|
key={col.name}
|
||||||
|
value={col.name}
|
||||||
|
onSelect={() => {
|
||||||
|
const newMappings = [...(component.componentConfig?.action?.fieldMappings || [])];
|
||||||
|
newMappings[index] = { ...newMappings[index], sourceField: col.name };
|
||||||
|
setModalActionFieldMappings(newMappings);
|
||||||
|
onUpdateProperty("componentConfig.action.fieldMappings", newMappings);
|
||||||
|
setModalFieldMappingSourceOpen((prev) => ({ ...prev, [index]: false }));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Check
|
||||||
|
className={cn("mr-2 h-4 w-4", mapping.sourceField === col.name ? "opacity-100" : "opacity-0")}
|
||||||
|
/>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span className="text-xs font-medium">{col.label}</span>
|
||||||
|
<span className="text-[10px] text-muted-foreground">{col.name}</span>
|
||||||
|
</div>
|
||||||
|
</CommandItem>
|
||||||
|
))}
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
|
||||||
|
<span className="text-xs text-muted-foreground">→</span>
|
||||||
|
|
||||||
|
{/* 대상 필드 선택 */}
|
||||||
|
<Popover
|
||||||
|
open={modalFieldMappingTargetOpen[index] || false}
|
||||||
|
onOpenChange={(open) => setModalFieldMappingTargetOpen((prev) => ({ ...prev, [index]: open }))}
|
||||||
|
>
|
||||||
|
<PopoverTrigger asChild>
|
||||||
|
<Button variant="outline" size="sm" className="h-7 flex-1 justify-between text-xs">
|
||||||
|
{mapping.targetField
|
||||||
|
? modalActionTargetColumns.find((c) => c.name === mapping.targetField)?.label || mapping.targetField
|
||||||
|
: "대상 컬럼 선택"}
|
||||||
|
<ChevronsUpDown className="ml-1 h-3 w-3 opacity-50" />
|
||||||
|
</Button>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent className="w-[200px] p-0" align="start">
|
||||||
|
<Command>
|
||||||
|
<CommandInput
|
||||||
|
placeholder="컬럼 검색..."
|
||||||
|
value={modalFieldMappingTargetSearch[index] || ""}
|
||||||
|
onValueChange={(val) => setModalFieldMappingTargetSearch((prev) => ({ ...prev, [index]: val }))}
|
||||||
|
/>
|
||||||
|
<CommandList>
|
||||||
|
<CommandEmpty>컬럼을 찾을 수 없습니다.</CommandEmpty>
|
||||||
|
<CommandGroup>
|
||||||
|
{modalActionTargetColumns
|
||||||
|
.filter((col) =>
|
||||||
|
col.name.toLowerCase().includes((modalFieldMappingTargetSearch[index] || "").toLowerCase()) ||
|
||||||
|
col.label.toLowerCase().includes((modalFieldMappingTargetSearch[index] || "").toLowerCase())
|
||||||
|
)
|
||||||
|
.map((col) => (
|
||||||
|
<CommandItem
|
||||||
|
key={col.name}
|
||||||
|
value={col.name}
|
||||||
|
onSelect={() => {
|
||||||
|
const newMappings = [...(component.componentConfig?.action?.fieldMappings || [])];
|
||||||
|
newMappings[index] = { ...newMappings[index], targetField: col.name };
|
||||||
|
setModalActionFieldMappings(newMappings);
|
||||||
|
onUpdateProperty("componentConfig.action.fieldMappings", newMappings);
|
||||||
|
setModalFieldMappingTargetOpen((prev) => ({ ...prev, [index]: false }));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Check
|
||||||
|
className={cn("mr-2 h-4 w-4", mapping.targetField === col.name ? "opacity-100" : "opacity-0")}
|
||||||
|
/>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span className="text-xs font-medium">{col.label}</span>
|
||||||
|
<span className="text-[10px] text-muted-foreground">{col.name}</span>
|
||||||
|
</div>
|
||||||
|
</CommandItem>
|
||||||
|
))}
|
||||||
|
</CommandGroup>
|
||||||
|
</CommandList>
|
||||||
|
</Command>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
|
|
||||||
|
{/* 삭제 버튼 */}
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="ghost"
|
||||||
|
size="sm"
|
||||||
|
className="h-7 w-7 p-0 text-destructive hover:bg-destructive/10"
|
||||||
|
onClick={() => {
|
||||||
|
const newMappings = (component.componentConfig?.action?.fieldMappings || []).filter((_: any, i: number) => i !== index);
|
||||||
|
setModalActionFieldMappings(newMappings);
|
||||||
|
onUpdateProperty("componentConfig.action.fieldMappings", newMappings);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<X className="h-3 w-3" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2069,10 +2069,6 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🆕 modalDataStore에 선택된 데이터 자동 저장 (테이블명 기반 dataSourceId)
|
// 🆕 modalDataStore에 선택된 데이터 자동 저장 (테이블명 기반 dataSourceId)
|
||||||
console.log("🔍 [TableList] modalDataStore 저장 조건:", {
|
|
||||||
selectedTable: tableConfig.selectedTable,
|
|
||||||
selectedRowsCount: selectedRowsData.length,
|
|
||||||
});
|
|
||||||
if (tableConfig.selectedTable && selectedRowsData.length > 0) {
|
if (tableConfig.selectedTable && selectedRowsData.length > 0) {
|
||||||
import("@/stores/modalDataStore").then(({ useModalDataStore }) => {
|
import("@/stores/modalDataStore").then(({ useModalDataStore }) => {
|
||||||
const modalItems = selectedRowsData.map((row, idx) => ({
|
const modalItems = selectedRowsData.map((row, idx) => ({
|
||||||
|
|
@ -2080,11 +2076,6 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||||
originalData: row,
|
originalData: row,
|
||||||
additionalData: {},
|
additionalData: {},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
console.log("✅ [TableList] modalDataStore에 데이터 저장:", {
|
|
||||||
sourceId: tableConfig.selectedTable,
|
|
||||||
itemCount: modalItems.length,
|
|
||||||
});
|
|
||||||
useModalDataStore.getState().setData(tableConfig.selectedTable!, modalItems);
|
useModalDataStore.getState().setData(tableConfig.selectedTable!, modalItems);
|
||||||
});
|
});
|
||||||
} else if (tableConfig.selectedTable && selectedRowsData.length === 0) {
|
} else if (tableConfig.selectedTable && selectedRowsData.length === 0) {
|
||||||
|
|
@ -2092,8 +2083,6 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
||||||
import("@/stores/modalDataStore").then(({ useModalDataStore }) => {
|
import("@/stores/modalDataStore").then(({ useModalDataStore }) => {
|
||||||
useModalDataStore.getState().clearData(tableConfig.selectedTable!);
|
useModalDataStore.getState().clearData(tableConfig.selectedTable!);
|
||||||
});
|
});
|
||||||
} else if (!tableConfig.selectedTable) {
|
|
||||||
console.warn("⚠️ [TableList] selectedTable이 없어 modalDataStore에 저장하지 않음");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const allRowsSelected = filteredData.every((row, index) => newSelectedRows.has(getRowKey(row, index)));
|
const allRowsSelected = filteredData.every((row, index) => newSelectedRows.has(getRowKey(row, index)));
|
||||||
|
|
|
||||||
|
|
@ -2629,25 +2629,10 @@ export class ButtonActionExecutor {
|
||||||
|
|
||||||
dataSourceId = dataSourceId || context.tableName || "default";
|
dataSourceId = dataSourceId || context.tableName || "default";
|
||||||
|
|
||||||
console.log("🔍 [handleModal] 데이터 조회 시도:", {
|
|
||||||
dataSourceId,
|
|
||||||
autoDetectDataSource,
|
|
||||||
hasAllComponents: !!context.allComponents,
|
|
||||||
allComponentsCount: context.allComponents?.length,
|
|
||||||
});
|
|
||||||
|
|
||||||
// modalDataStore에서 데이터 가져오기
|
// modalDataStore에서 데이터 가져오기
|
||||||
try {
|
try {
|
||||||
const { useModalDataStore } = await import("@/stores/modalDataStore");
|
const { useModalDataStore } = await import("@/stores/modalDataStore");
|
||||||
const dataRegistry = useModalDataStore.getState().dataRegistry;
|
const dataRegistry = useModalDataStore.getState().dataRegistry;
|
||||||
|
|
||||||
console.log("📦 [handleModal] modalDataStore 상태:", {
|
|
||||||
registryKeys: Object.keys(dataRegistry),
|
|
||||||
targetKey: dataSourceId,
|
|
||||||
hasData: !!dataRegistry[dataSourceId],
|
|
||||||
dataLength: dataRegistry[dataSourceId]?.length || 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
const modalData = dataRegistry[dataSourceId] || [];
|
const modalData = dataRegistry[dataSourceId] || [];
|
||||||
|
|
||||||
if (modalData.length === 0) {
|
if (modalData.length === 0) {
|
||||||
|
|
@ -2658,15 +2643,19 @@ export class ButtonActionExecutor {
|
||||||
|
|
||||||
selectedData = modalData.map((item: any) => item.originalData || item);
|
selectedData = modalData.map((item: any) => item.originalData || item);
|
||||||
const rawParentData = modalData[0]?.originalData || modalData[0] || {};
|
const rawParentData = modalData[0]?.originalData || modalData[0] || {};
|
||||||
parentData = { ...rawParentData };
|
|
||||||
|
|
||||||
// 필드 매핑 적용
|
// 필드 매핑 적용
|
||||||
if (config.fieldMappings?.length) {
|
if (config.fieldMappings?.length) {
|
||||||
|
// 매핑이 있으면 매핑된 필드만 전달
|
||||||
|
parentData = {};
|
||||||
config.fieldMappings.forEach((mapping) => {
|
config.fieldMappings.forEach((mapping) => {
|
||||||
if (mapping.sourceField && mapping.targetField && rawParentData[mapping.sourceField] !== undefined) {
|
if (mapping.sourceField && mapping.targetField && rawParentData[mapping.sourceField] !== undefined) {
|
||||||
parentData[mapping.targetField] = rawParentData[mapping.sourceField];
|
parentData[mapping.targetField] = rawParentData[mapping.sourceField];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
// 매핑이 없으면 모든 필드 전달 (동일 컬럼명 자동 매핑)
|
||||||
|
parentData = { ...rawParentData };
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("❌ 데이터 확인 실패:", error);
|
console.error("❌ 데이터 확인 실패:", error);
|
||||||
|
|
@ -2676,16 +2665,20 @@ export class ButtonActionExecutor {
|
||||||
} else {
|
} else {
|
||||||
// 2-2. 기본 모드: context에서 직접 가져오기
|
// 2-2. 기본 모드: context에서 직접 가져오기
|
||||||
selectedData = context.selectedRowsData || [];
|
selectedData = context.selectedRowsData || [];
|
||||||
parentData = context.splitPanelParentData || {};
|
const rawParentData = context.splitPanelParentData || selectedData[0] || {};
|
||||||
|
|
||||||
// 필드 매핑 적용
|
// 필드 매핑 적용
|
||||||
if (config.fieldMappings?.length && selectedData.length > 0) {
|
if (config.fieldMappings?.length && selectedData.length > 0) {
|
||||||
const rawParentData = selectedData[0] || {};
|
// 매핑이 있으면 매핑된 필드만 전달
|
||||||
|
parentData = {};
|
||||||
config.fieldMappings.forEach((mapping) => {
|
config.fieldMappings.forEach((mapping) => {
|
||||||
if (mapping.sourceField && mapping.targetField && rawParentData[mapping.sourceField] !== undefined) {
|
if (mapping.sourceField && mapping.targetField && rawParentData[mapping.sourceField] !== undefined) {
|
||||||
parentData[mapping.targetField] = rawParentData[mapping.sourceField];
|
parentData[mapping.targetField] = rawParentData[mapping.sourceField];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
// 매핑이 없으면 모든 필드 전달 (동일 컬럼명 자동 매핑)
|
||||||
|
parentData = { ...rawParentData };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2749,14 +2742,6 @@ export class ButtonActionExecutor {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. 모달 열기 이벤트 발생
|
// 4. 모달 열기 이벤트 발생
|
||||||
console.log("🚀 [handleModal] 모달 열기 이벤트 발생:", {
|
|
||||||
screenId: config.targetScreenId,
|
|
||||||
title: finalTitle,
|
|
||||||
selectedDataCount: selectedData.length,
|
|
||||||
parentDataKeys: Object.keys(parentData),
|
|
||||||
parentData: parentData,
|
|
||||||
});
|
|
||||||
|
|
||||||
// passSelectedData가 true이면 editData로 전달 (수정 모드처럼 모든 필드 표시)
|
// passSelectedData가 true이면 editData로 전달 (수정 모드처럼 모든 필드 표시)
|
||||||
const isPassDataMode = passSelectedData && selectedData.length > 0;
|
const isPassDataMode = passSelectedData && selectedData.length > 0;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue