화면간 데이터 전달기능 구현
This commit is contained in:
@@ -15,7 +15,7 @@ import { getTableColumns } from "@/lib/api/tableManagement";
|
||||
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||
import { cn } from "@/lib/utils";
|
||||
import type { ParentDataMapping } from "@/contexts/SplitPanelContext";
|
||||
import type { ParentDataMapping, LinkedFilter } from "@/contexts/SplitPanelContext";
|
||||
|
||||
interface ScreenSplitPanelConfigPanelProps {
|
||||
config: any;
|
||||
@@ -33,7 +33,15 @@ export function ScreenSplitPanelConfigPanel({ config = {}, onChange }: ScreenSpl
|
||||
|
||||
// 좌측 화면의 테이블 컬럼 목록
|
||||
const [leftScreenColumns, setLeftScreenColumns] = useState<Array<{ columnName: string; columnLabel: string }>>([]);
|
||||
const [isLoadingColumns, setIsLoadingColumns] = useState(false);
|
||||
const [isLoadingLeftColumns, setIsLoadingLeftColumns] = useState(false);
|
||||
|
||||
// 우측 화면의 테이블 컬럼 목록 (테이블별로 그룹화)
|
||||
const [rightScreenTables, setRightScreenTables] = useState<Array<{
|
||||
tableName: string;
|
||||
screenName: string;
|
||||
columns: Array<{ columnName: string; columnLabel: string }>
|
||||
}>>([]);
|
||||
const [isLoadingRightColumns, setIsLoadingRightColumns] = useState(false);
|
||||
|
||||
const [localConfig, setLocalConfig] = useState({
|
||||
screenId: config.screenId || 0,
|
||||
@@ -44,6 +52,7 @@ export function ScreenSplitPanelConfigPanel({ config = {}, onChange }: ScreenSpl
|
||||
buttonLabel: config.buttonLabel || "데이터 전달",
|
||||
buttonPosition: config.buttonPosition || "center",
|
||||
parentDataMapping: config.parentDataMapping || [] as ParentDataMapping[],
|
||||
linkedFilters: config.linkedFilters || [] as LinkedFilter[],
|
||||
...config,
|
||||
});
|
||||
|
||||
@@ -59,6 +68,7 @@ export function ScreenSplitPanelConfigPanel({ config = {}, onChange }: ScreenSpl
|
||||
buttonLabel: config.buttonLabel || "데이터 전달",
|
||||
buttonPosition: config.buttonPosition || "center",
|
||||
parentDataMapping: config.parentDataMapping || [],
|
||||
linkedFilters: config.linkedFilters || [],
|
||||
...config,
|
||||
});
|
||||
}, [config]);
|
||||
@@ -72,7 +82,7 @@ export function ScreenSplitPanelConfigPanel({ config = {}, onChange }: ScreenSpl
|
||||
}
|
||||
|
||||
try {
|
||||
setIsLoadingColumns(true);
|
||||
setIsLoadingLeftColumns(true);
|
||||
|
||||
// 좌측 화면 정보 조회
|
||||
const screenData = await screenApi.getScreen(localConfig.leftScreenId);
|
||||
@@ -96,13 +106,126 @@ export function ScreenSplitPanelConfigPanel({ config = {}, onChange }: ScreenSpl
|
||||
console.error("좌측 화면 컬럼 로드 실패:", error);
|
||||
setLeftScreenColumns([]);
|
||||
} finally {
|
||||
setIsLoadingColumns(false);
|
||||
setIsLoadingLeftColumns(false);
|
||||
}
|
||||
};
|
||||
|
||||
loadLeftScreenColumns();
|
||||
}, [localConfig.leftScreenId]);
|
||||
|
||||
// 우측 화면이 변경되면 해당 화면 및 임베드된 화면들의 테이블 컬럼 로드
|
||||
useEffect(() => {
|
||||
const loadRightScreenColumns = async () => {
|
||||
if (!localConfig.rightScreenId) {
|
||||
setRightScreenTables([]);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsLoadingRightColumns(true);
|
||||
const tables: Array<{ tableName: string; screenName: string; columns: Array<{ columnName: string; columnLabel: string }> }> = [];
|
||||
|
||||
// 우측 화면 정보 조회
|
||||
const screenData = await screenApi.getScreen(localConfig.rightScreenId);
|
||||
|
||||
// 1. 메인 화면의 테이블 (있는 경우)
|
||||
if (screenData?.tableName) {
|
||||
const columnsResponse = await getTableColumns(screenData.tableName);
|
||||
if (columnsResponse.success && columnsResponse.data?.columns) {
|
||||
tables.push({
|
||||
tableName: screenData.tableName,
|
||||
screenName: screenData.screenName || "메인 화면",
|
||||
columns: columnsResponse.data.columns.map((col: any) => ({
|
||||
columnName: col.column_name || col.columnName,
|
||||
columnLabel: col.column_label || col.columnLabel || col.column_name || col.columnName,
|
||||
})),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 레이아웃에서 임베드된 화면들의 테이블 찾기 (탭, 분할 패널 등)
|
||||
const layoutData = await screenApi.getLayout(localConfig.rightScreenId);
|
||||
const components = layoutData?.components || [];
|
||||
|
||||
if (components.length > 0) {
|
||||
const embeddedScreenIds = new Set<number>();
|
||||
|
||||
// 컴포넌트에서 임베드된 화면 ID 수집
|
||||
const findEmbeddedScreens = (comps: any[]) => {
|
||||
for (const comp of comps) {
|
||||
const config = comp.componentConfig || {};
|
||||
|
||||
// TabsWidget의 탭들
|
||||
if (comp.componentType === "tabs-widget" && config.tabs) {
|
||||
for (const tab of config.tabs) {
|
||||
if (tab.screenId) {
|
||||
embeddedScreenIds.add(tab.screenId);
|
||||
console.log("🔍 탭에서 화면 발견:", tab.screenId, tab.screenName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ScreenSplitPanel
|
||||
if (comp.componentType === "screen-split-panel") {
|
||||
if (config.leftScreenId) embeddedScreenIds.add(config.leftScreenId);
|
||||
if (config.rightScreenId) embeddedScreenIds.add(config.rightScreenId);
|
||||
}
|
||||
|
||||
// EmbeddedScreen
|
||||
if (comp.componentType === "embedded-screen" && config.screenId) {
|
||||
embeddedScreenIds.add(config.screenId);
|
||||
}
|
||||
|
||||
// 중첩된 컴포넌트 검색
|
||||
if (comp.children) {
|
||||
findEmbeddedScreens(comp.children);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
findEmbeddedScreens(components);
|
||||
console.log("📋 발견된 임베드 화면 ID:", Array.from(embeddedScreenIds));
|
||||
|
||||
// 임베드된 화면들의 테이블 컬럼 로드
|
||||
for (const embeddedScreenId of embeddedScreenIds) {
|
||||
try {
|
||||
const embeddedScreen = await screenApi.getScreen(embeddedScreenId);
|
||||
if (embeddedScreen?.tableName) {
|
||||
// 이미 추가된 테이블인지 확인
|
||||
if (!tables.find(t => t.tableName === embeddedScreen.tableName)) {
|
||||
const columnsResponse = await getTableColumns(embeddedScreen.tableName);
|
||||
if (columnsResponse.success && columnsResponse.data?.columns) {
|
||||
tables.push({
|
||||
tableName: embeddedScreen.tableName,
|
||||
screenName: embeddedScreen.screenName || `화면 ${embeddedScreenId}`,
|
||||
columns: columnsResponse.data.columns.map((col: any) => ({
|
||||
columnName: col.column_name || col.columnName,
|
||||
columnLabel: col.column_label || col.columnLabel || col.column_name || col.columnName,
|
||||
})),
|
||||
});
|
||||
console.log("✅ 테이블 추가:", embeddedScreen.tableName);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(`임베드된 화면 ${embeddedScreenId} 로드 실패:`, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setRightScreenTables(tables);
|
||||
console.log("📋 우측 화면 테이블 로드 완료:", tables.map(t => t.tableName));
|
||||
} catch (error) {
|
||||
console.error("우측 화면 컬럼 로드 실패:", error);
|
||||
setRightScreenTables([]);
|
||||
} finally {
|
||||
setIsLoadingRightColumns(false);
|
||||
}
|
||||
};
|
||||
|
||||
loadRightScreenColumns();
|
||||
}, [localConfig.rightScreenId]);
|
||||
|
||||
// 화면 목록 로드
|
||||
useEffect(() => {
|
||||
const loadScreens = async () => {
|
||||
@@ -168,21 +291,51 @@ export function ScreenSplitPanelConfigPanel({ config = {}, onChange }: ScreenSpl
|
||||
updateConfig("parentDataMapping", newMappings);
|
||||
};
|
||||
|
||||
// 연결 필터 추가
|
||||
const addLinkedFilter = () => {
|
||||
const newFilter: LinkedFilter = {
|
||||
sourceColumn: "",
|
||||
targetColumn: "",
|
||||
};
|
||||
const newFilters = [...(localConfig.linkedFilters || []), newFilter];
|
||||
updateConfig("linkedFilters", newFilters);
|
||||
};
|
||||
|
||||
// 연결 필터 수정
|
||||
const updateLinkedFilter = (index: number, field: keyof LinkedFilter, value: string) => {
|
||||
const newFilters = [...(localConfig.linkedFilters || [])];
|
||||
newFilters[index] = {
|
||||
...newFilters[index],
|
||||
[field]: value,
|
||||
};
|
||||
updateConfig("linkedFilters", newFilters);
|
||||
};
|
||||
|
||||
// 연결 필터 삭제
|
||||
const removeLinkedFilter = (index: number) => {
|
||||
const newFilters = (localConfig.linkedFilters || []).filter((_: any, i: number) => i !== index);
|
||||
updateConfig("linkedFilters", newFilters);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<Tabs defaultValue="layout" className="w-full">
|
||||
<TabsList className="grid w-full grid-cols-3">
|
||||
<TabsTrigger value="layout" className="gap-2">
|
||||
<Layout className="h-4 w-4" />
|
||||
<TabsList className="grid w-full grid-cols-4">
|
||||
<TabsTrigger value="layout" className="gap-1 text-xs">
|
||||
<Layout className="h-3 w-3" />
|
||||
레이아웃
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="screens" className="gap-2">
|
||||
<Database className="h-4 w-4" />
|
||||
화면 설정
|
||||
<TabsTrigger value="screens" className="gap-1 text-xs">
|
||||
<Database className="h-3 w-3" />
|
||||
화면
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="dataMapping" className="gap-2">
|
||||
<Link2 className="h-4 w-4" />
|
||||
데이터 전달
|
||||
<TabsTrigger value="linkedFilter" className="gap-1 text-xs">
|
||||
<Link2 className="h-3 w-3" />
|
||||
연결필터
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="dataMapping" className="gap-1 text-xs">
|
||||
<ArrowRight className="h-3 w-3" />
|
||||
데이터전달
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
@@ -385,6 +538,141 @@ export function ScreenSplitPanelConfigPanel({ config = {}, onChange }: ScreenSpl
|
||||
</Card>
|
||||
</TabsContent>
|
||||
|
||||
{/* 연결 필터 탭 */}
|
||||
<TabsContent value="linkedFilter" className="space-y-4">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">연결 필터</CardTitle>
|
||||
<CardDescription className="text-xs">
|
||||
좌측 화면에서 행을 선택하면, 우측 화면의 테이블이 자동으로 필터링됩니다.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
{!localConfig.leftScreenId || !localConfig.rightScreenId ? (
|
||||
<div className="rounded-lg border border-amber-200 bg-amber-50 p-3 dark:border-amber-900 dark:bg-amber-950">
|
||||
<p className="text-xs text-amber-800 dark:text-amber-200">
|
||||
먼저 "화면" 탭에서 좌측/우측 화면을 모두 선택하세요.
|
||||
</p>
|
||||
</div>
|
||||
) : isLoadingLeftColumns || isLoadingRightColumns ? (
|
||||
<div className="flex items-center justify-center py-4">
|
||||
<Loader2 className="text-muted-foreground h-5 w-5 animate-spin" />
|
||||
<span className="text-muted-foreground ml-2 text-xs">컬럼 정보 로딩 중...</span>
|
||||
</div>
|
||||
) : leftScreenColumns.length === 0 || rightScreenTables.length === 0 ? (
|
||||
<div className="rounded-lg border border-amber-200 bg-amber-50 p-3 dark:border-amber-900 dark:bg-amber-950">
|
||||
<p className="text-xs text-amber-800 dark:text-amber-200">
|
||||
{leftScreenColumns.length === 0 && "좌측 화면에 테이블이 설정되지 않았습니다. "}
|
||||
{rightScreenTables.length === 0 && "우측 화면에 테이블이 설정되지 않았습니다."}
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{/* 연결 필터 설명 */}
|
||||
<div className="rounded-lg border border-blue-200 bg-blue-50 p-3 dark:border-blue-900 dark:bg-blue-950">
|
||||
<p className="text-xs text-blue-800 dark:text-blue-200">
|
||||
예: 좌측에서 설비를 선택하면 → 우측 점검항목이 해당 설비의 항목만 표시됩니다.
|
||||
<br />
|
||||
좌측 <code className="bg-blue-100 dark:bg-blue-900 px-1 rounded">equipment_code</code> →
|
||||
우측 <code className="bg-blue-100 dark:bg-blue-900 px-1 rounded">equipment_code</code>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 필터 목록 */}
|
||||
<div className="space-y-3">
|
||||
{(localConfig.linkedFilters || []).map((filter: LinkedFilter, index: number) => (
|
||||
<div key={index} className="rounded-lg border bg-gray-50 p-3 dark:bg-gray-900">
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<span className="text-xs font-medium text-gray-700">필터 #{index + 1}</span>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => removeLinkedFilter(index)}
|
||||
className="h-6 w-6 p-0 text-red-500 hover:bg-red-50 hover:text-red-600"
|
||||
>
|
||||
<Trash2 className="h-3 w-3" />
|
||||
</Button>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div>
|
||||
<Label className="text-xs text-gray-600">소스 컬럼 (좌측 화면)</Label>
|
||||
<Select
|
||||
value={filter.sourceColumn}
|
||||
onValueChange={(value) => updateLinkedFilter(index, "sourceColumn", value)}
|
||||
>
|
||||
<SelectTrigger className="h-8 text-xs">
|
||||
<SelectValue placeholder="컬럼 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{leftScreenColumns.map((col) => (
|
||||
<SelectItem key={col.columnName} value={col.columnName} className="text-xs">
|
||||
{col.columnLabel} ({col.columnName})
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="flex justify-center">
|
||||
<ArrowRight className="h-4 w-4 rotate-90 text-gray-400" />
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-xs text-gray-600">타겟 컬럼 (우측 화면 필터)</Label>
|
||||
<Select
|
||||
value={filter.targetColumn}
|
||||
onValueChange={(value) => updateLinkedFilter(index, "targetColumn", value)}
|
||||
>
|
||||
<SelectTrigger className="h-8 text-xs">
|
||||
<SelectValue placeholder="테이블.컬럼 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{rightScreenTables.map((table) => (
|
||||
<React.Fragment key={table.tableName}>
|
||||
<div className="px-2 py-1.5 text-xs font-semibold text-gray-500 bg-gray-100 dark:bg-gray-800">
|
||||
{table.screenName} ({table.tableName})
|
||||
</div>
|
||||
{table.columns.map((col) => (
|
||||
<SelectItem
|
||||
key={`${table.tableName}.${col.columnName}`}
|
||||
value={`${table.tableName}.${col.columnName}`}
|
||||
className="text-xs"
|
||||
>
|
||||
{col.columnLabel} ({col.columnName})
|
||||
</SelectItem>
|
||||
))}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* 추가 버튼 */}
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={addLinkedFilter}
|
||||
className="w-full text-xs"
|
||||
>
|
||||
<Plus className="mr-2 h-3 w-3" />
|
||||
연결 필터 추가
|
||||
</Button>
|
||||
|
||||
{/* 현재 설정 표시 */}
|
||||
<Separator />
|
||||
<div className="text-xs text-muted-foreground">
|
||||
{(localConfig.linkedFilters || []).length > 0
|
||||
? `${localConfig.linkedFilters.length}개 필터 설정됨`
|
||||
: "필터 없음 - 우측 화면에 모든 데이터가 표시됩니다"}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
|
||||
{/* 데이터 전달 탭 */}
|
||||
<TabsContent value="dataMapping" className="space-y-4">
|
||||
<Card>
|
||||
@@ -395,69 +683,105 @@ export function ScreenSplitPanelConfigPanel({ config = {}, onChange }: ScreenSpl
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
{!localConfig.leftScreenId ? (
|
||||
{!localConfig.leftScreenId || !localConfig.rightScreenId ? (
|
||||
<div className="rounded-lg border border-amber-200 bg-amber-50 p-3 dark:border-amber-900 dark:bg-amber-950">
|
||||
<p className="text-xs text-amber-800 dark:text-amber-200">
|
||||
먼저 "화면 설정" 탭에서 좌측 화면을 선택하세요.
|
||||
먼저 "화면 설정" 탭에서 좌측/우측 화면을 모두 선택하세요.
|
||||
</p>
|
||||
</div>
|
||||
) : isLoadingColumns ? (
|
||||
) : isLoadingLeftColumns || isLoadingRightColumns ? (
|
||||
<div className="flex items-center justify-center py-4">
|
||||
<Loader2 className="text-muted-foreground h-5 w-5 animate-spin" />
|
||||
<span className="text-muted-foreground ml-2 text-xs">컬럼 정보 로딩 중...</span>
|
||||
</div>
|
||||
) : leftScreenColumns.length === 0 ? (
|
||||
) : leftScreenColumns.length === 0 || rightScreenTables.length === 0 ? (
|
||||
<div className="rounded-lg border border-amber-200 bg-amber-50 p-3 dark:border-amber-900 dark:bg-amber-950">
|
||||
<p className="text-xs text-amber-800 dark:text-amber-200">
|
||||
좌측 화면에 테이블이 설정되지 않았거나 컬럼 정보를 불러올 수 없습니다.
|
||||
{leftScreenColumns.length === 0 && "좌측 화면에 테이블이 설정되지 않았습니다. "}
|
||||
{rightScreenTables.length === 0 && "우측 화면에 테이블이 설정되지 않았습니다."}
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{/* 우측 화면 테이블 목록 표시 */}
|
||||
<div className="rounded-lg border border-green-200 bg-green-50 p-3 dark:border-green-900 dark:bg-green-950">
|
||||
<p className="text-xs font-medium text-green-800 dark:text-green-200 mb-1">
|
||||
우측 화면에서 감지된 테이블 ({rightScreenTables.length}개):
|
||||
</p>
|
||||
<ul className="text-xs text-green-700 dark:text-green-300 space-y-0.5">
|
||||
{rightScreenTables.map((table) => (
|
||||
<li key={table.tableName}>• {table.screenName}: <code className="bg-green-100 dark:bg-green-900 px-1 rounded">{table.tableName}</code></li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* 매핑 목록 */}
|
||||
<div className="space-y-3">
|
||||
{(localConfig.parentDataMapping || []).map((mapping: ParentDataMapping, index: number) => (
|
||||
<div key={index} className="flex items-center gap-2 rounded-lg border bg-gray-50 p-3 dark:bg-gray-900">
|
||||
<div className="flex-1 space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex-1">
|
||||
<Label className="text-xs text-gray-600">소스 컬럼 (좌측)</Label>
|
||||
<Select
|
||||
value={mapping.sourceColumn}
|
||||
onValueChange={(value) => updateParentDataMapping(index, "sourceColumn", value)}
|
||||
>
|
||||
<SelectTrigger className="h-8 text-xs">
|
||||
<SelectValue placeholder="컬럼 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{leftScreenColumns.map((col) => (
|
||||
<SelectItem key={col.columnName} value={col.columnName} className="text-xs">
|
||||
{col.columnLabel} ({col.columnName})
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<ArrowRight className="mt-5 h-4 w-4 text-gray-400" />
|
||||
<div className="flex-1">
|
||||
<Label className="text-xs text-gray-600">타겟 컬럼 (우측 저장 시)</Label>
|
||||
<Input
|
||||
value={mapping.targetColumn}
|
||||
onChange={(e) => updateParentDataMapping(index, "targetColumn", e.target.value)}
|
||||
placeholder="저장할 컬럼명"
|
||||
className="h-8 text-xs"
|
||||
/>
|
||||
</div>
|
||||
<div key={index} className="rounded-lg border bg-gray-50 p-3 dark:bg-gray-900">
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<span className="text-xs font-medium text-gray-700">매핑 #{index + 1}</span>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => removeParentDataMapping(index)}
|
||||
className="h-6 w-6 p-0 text-red-500 hover:bg-red-50 hover:text-red-600"
|
||||
>
|
||||
<Trash2 className="h-3 w-3" />
|
||||
</Button>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div>
|
||||
<Label className="text-xs text-gray-600">소스 컬럼 (좌측 화면)</Label>
|
||||
<Select
|
||||
value={mapping.sourceColumn}
|
||||
onValueChange={(value) => updateParentDataMapping(index, "sourceColumn", value)}
|
||||
>
|
||||
<SelectTrigger className="h-8 text-xs">
|
||||
<SelectValue placeholder="컬럼 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{leftScreenColumns.map((col) => (
|
||||
<SelectItem key={col.columnName} value={col.columnName} className="text-xs">
|
||||
{col.columnLabel} ({col.columnName})
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="flex justify-center">
|
||||
<ArrowRight className="h-4 w-4 rotate-90 text-gray-400" />
|
||||
</div>
|
||||
<div>
|
||||
<Label className="text-xs text-gray-600">타겟 컬럼 (우측 화면)</Label>
|
||||
<Select
|
||||
value={mapping.targetColumn}
|
||||
onValueChange={(value) => updateParentDataMapping(index, "targetColumn", value)}
|
||||
>
|
||||
<SelectTrigger className="h-8 text-xs">
|
||||
<SelectValue placeholder="테이블.컬럼 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{rightScreenTables.map((table) => (
|
||||
<React.Fragment key={table.tableName}>
|
||||
<div className="px-2 py-1.5 text-xs font-semibold text-gray-500 bg-gray-100 dark:bg-gray-800">
|
||||
{table.screenName} ({table.tableName})
|
||||
</div>
|
||||
{table.columns.map((col) => (
|
||||
<SelectItem
|
||||
key={`${table.tableName}.${col.columnName}`}
|
||||
value={col.columnName}
|
||||
className="text-xs pl-4"
|
||||
>
|
||||
{col.columnLabel} ({col.columnName})
|
||||
</SelectItem>
|
||||
))}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => removeParentDataMapping(index)}
|
||||
className="h-8 w-8 p-0 text-red-500 hover:bg-red-50 hover:text-red-600"
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -473,23 +797,23 @@ export function ScreenSplitPanelConfigPanel({ config = {}, onChange }: ScreenSpl
|
||||
매핑 추가
|
||||
</Button>
|
||||
|
||||
{/* 안내 메시지 */}
|
||||
{/* 자동 매핑 안내 */}
|
||||
<div className="rounded-lg border border-green-200 bg-green-50 p-3 dark:border-green-900 dark:bg-green-950">
|
||||
<p className="text-xs text-green-800 dark:text-green-200">
|
||||
<strong>자동 매핑:</strong> 좌측에서 선택한 데이터의 모든 컬럼이 우측 화면에 자동 전달됩니다.
|
||||
<br />
|
||||
동일한 컬럼명(예: equipment_code)이 있으면 별도 설정 없이 자동으로 매핑됩니다.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 수동 매핑 안내 */}
|
||||
<div className="rounded-lg border border-blue-200 bg-blue-50 p-3 dark:border-blue-900 dark:bg-blue-950">
|
||||
<p className="text-xs text-blue-800 dark:text-blue-200">
|
||||
<strong>사용 예시:</strong>
|
||||
<strong>수동 매핑 (선택사항):</strong>
|
||||
<br />
|
||||
좌측: 설비 목록 (equipment_mng)
|
||||
컬럼명이 다른 경우에만 위에서 매핑을 추가하세요.
|
||||
<br />
|
||||
우측: 점검항목 추가 화면
|
||||
<br />
|
||||
<br />
|
||||
매핑 설정:
|
||||
<br />
|
||||
- 소스: equipment_code → 타겟: equipment_code
|
||||
<br />
|
||||
<br />
|
||||
좌측에서 설비를 선택하고 우측에서 점검항목을 추가하면,
|
||||
선택한 설비의 equipment_code가 자동으로 저장됩니다.
|
||||
예: 좌측 <code className="bg-blue-100 px-1 rounded">user_id</code> → 우측 <code className="bg-blue-100 px-1 rounded">created_by</code>
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user