카드 컴포넌트 중간커밋
This commit is contained in:
@@ -9,11 +9,13 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { Settings, Layout, ArrowRight, Database, Loader2, Check, ChevronsUpDown } from "lucide-react";
|
||||
import { Settings, Layout, ArrowRight, Database, Loader2, Check, ChevronsUpDown, Plus, Trash2, Link2 } from "lucide-react";
|
||||
import { screenApi } from "@/lib/api/screen";
|
||||
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";
|
||||
|
||||
interface ScreenSplitPanelConfigPanelProps {
|
||||
config: any;
|
||||
@@ -29,6 +31,10 @@ export function ScreenSplitPanelConfigPanel({ config = {}, onChange }: ScreenSpl
|
||||
const [leftOpen, setLeftOpen] = useState(false);
|
||||
const [rightOpen, setRightOpen] = useState(false);
|
||||
|
||||
// 좌측 화면의 테이블 컬럼 목록
|
||||
const [leftScreenColumns, setLeftScreenColumns] = useState<Array<{ columnName: string; columnLabel: string }>>([]);
|
||||
const [isLoadingColumns, setIsLoadingColumns] = useState(false);
|
||||
|
||||
const [localConfig, setLocalConfig] = useState({
|
||||
screenId: config.screenId || 0,
|
||||
leftScreenId: config.leftScreenId || 0,
|
||||
@@ -37,6 +43,7 @@ export function ScreenSplitPanelConfigPanel({ config = {}, onChange }: ScreenSpl
|
||||
resizable: config.resizable ?? true,
|
||||
buttonLabel: config.buttonLabel || "데이터 전달",
|
||||
buttonPosition: config.buttonPosition || "center",
|
||||
parentDataMapping: config.parentDataMapping || [] as ParentDataMapping[],
|
||||
...config,
|
||||
});
|
||||
|
||||
@@ -51,10 +58,51 @@ export function ScreenSplitPanelConfigPanel({ config = {}, onChange }: ScreenSpl
|
||||
resizable: config.resizable ?? true,
|
||||
buttonLabel: config.buttonLabel || "데이터 전달",
|
||||
buttonPosition: config.buttonPosition || "center",
|
||||
parentDataMapping: config.parentDataMapping || [],
|
||||
...config,
|
||||
});
|
||||
}, [config]);
|
||||
|
||||
// 좌측 화면이 변경되면 해당 화면의 테이블 컬럼 로드
|
||||
useEffect(() => {
|
||||
const loadLeftScreenColumns = async () => {
|
||||
if (!localConfig.leftScreenId) {
|
||||
setLeftScreenColumns([]);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsLoadingColumns(true);
|
||||
|
||||
// 좌측 화면 정보 조회
|
||||
const screenData = await screenApi.getScreen(localConfig.leftScreenId);
|
||||
if (!screenData?.tableName) {
|
||||
console.warn("좌측 화면에 테이블이 설정되지 않았습니다.");
|
||||
setLeftScreenColumns([]);
|
||||
return;
|
||||
}
|
||||
|
||||
// 테이블 컬럼 조회
|
||||
const columnsResponse = await getTableColumns(screenData.tableName);
|
||||
if (columnsResponse.success && columnsResponse.data?.columns) {
|
||||
const columns = columnsResponse.data.columns.map((col: any) => ({
|
||||
columnName: col.column_name || col.columnName,
|
||||
columnLabel: col.column_label || col.columnLabel || col.column_name || col.columnName,
|
||||
}));
|
||||
setLeftScreenColumns(columns);
|
||||
console.log("📋 좌측 화면 컬럼 로드 완료:", columns.length);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("좌측 화면 컬럼 로드 실패:", error);
|
||||
setLeftScreenColumns([]);
|
||||
} finally {
|
||||
setIsLoadingColumns(false);
|
||||
}
|
||||
};
|
||||
|
||||
loadLeftScreenColumns();
|
||||
}, [localConfig.leftScreenId]);
|
||||
|
||||
// 화면 목록 로드
|
||||
useEffect(() => {
|
||||
const loadScreens = async () => {
|
||||
@@ -94,10 +142,36 @@ export function ScreenSplitPanelConfigPanel({ config = {}, onChange }: ScreenSpl
|
||||
}
|
||||
};
|
||||
|
||||
// 부모 데이터 매핑 추가
|
||||
const addParentDataMapping = () => {
|
||||
const newMapping: ParentDataMapping = {
|
||||
sourceColumn: "",
|
||||
targetColumn: "",
|
||||
};
|
||||
const newMappings = [...(localConfig.parentDataMapping || []), newMapping];
|
||||
updateConfig("parentDataMapping", newMappings);
|
||||
};
|
||||
|
||||
// 부모 데이터 매핑 수정
|
||||
const updateParentDataMapping = (index: number, field: keyof ParentDataMapping, value: string) => {
|
||||
const newMappings = [...(localConfig.parentDataMapping || [])];
|
||||
newMappings[index] = {
|
||||
...newMappings[index],
|
||||
[field]: value,
|
||||
};
|
||||
updateConfig("parentDataMapping", newMappings);
|
||||
};
|
||||
|
||||
// 부모 데이터 매핑 삭제
|
||||
const removeParentDataMapping = (index: number) => {
|
||||
const newMappings = (localConfig.parentDataMapping || []).filter((_: any, i: number) => i !== index);
|
||||
updateConfig("parentDataMapping", newMappings);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<Tabs defaultValue="layout" className="w-full">
|
||||
<TabsList className="grid w-full grid-cols-2">
|
||||
<TabsList className="grid w-full grid-cols-3">
|
||||
<TabsTrigger value="layout" className="gap-2">
|
||||
<Layout className="h-4 w-4" />
|
||||
레이아웃
|
||||
@@ -106,6 +180,10 @@ export function ScreenSplitPanelConfigPanel({ config = {}, onChange }: ScreenSpl
|
||||
<Database className="h-4 w-4" />
|
||||
화면 설정
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="dataMapping" className="gap-2">
|
||||
<Link2 className="h-4 w-4" />
|
||||
데이터 전달
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
{/* 레이아웃 탭 */}
|
||||
@@ -295,7 +373,7 @@ export function ScreenSplitPanelConfigPanel({ config = {}, onChange }: ScreenSpl
|
||||
|
||||
<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">
|
||||
💡 <strong>데이터 전달 방법:</strong> 좌측 화면에 테이블과 버튼을 배치하고, 버튼의 액션을
|
||||
데이터 전달 방법: 좌측 화면에 테이블과 버튼을 배치하고, 버튼의 액션을
|
||||
"transferData"로 설정하세요.
|
||||
<br />
|
||||
버튼 설정에서 소스 컴포넌트(테이블), 타겟 화면, 필드 매핑을 지정할 수 있습니다.
|
||||
@@ -306,6 +384,119 @@ export function ScreenSplitPanelConfigPanel({ config = {}, onChange }: ScreenSpl
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
|
||||
{/* 데이터 전달 탭 */}
|
||||
<TabsContent value="dataMapping" className="space-y-4">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base">부모 데이터 자동 전달</CardTitle>
|
||||
<CardDescription className="text-xs">
|
||||
좌측 화면에서 행을 선택하면, 우측 화면의 추가/저장 시 지정된 컬럼 값이 자동으로 포함됩니다.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
{!localConfig.leftScreenId ? (
|
||||
<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 ? (
|
||||
<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 ? (
|
||||
<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>
|
||||
) : (
|
||||
<>
|
||||
{/* 매핑 목록 */}
|
||||
<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>
|
||||
</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>
|
||||
|
||||
{/* 매핑 추가 버튼 */}
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={addParentDataMapping}
|
||||
className="w-full"
|
||||
>
|
||||
<Plus className="mr-2 h-4 w-4" />
|
||||
매핑 추가
|
||||
</Button>
|
||||
|
||||
{/* 안내 메시지 */}
|
||||
<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>
|
||||
<br />
|
||||
좌측: 설비 목록 (equipment_mng)
|
||||
<br />
|
||||
우측: 점검항목 추가 화면
|
||||
<br />
|
||||
<br />
|
||||
매핑 설정:
|
||||
<br />
|
||||
- 소스: equipment_code → 타겟: equipment_code
|
||||
<br />
|
||||
<br />
|
||||
좌측에서 설비를 선택하고 우측에서 점검항목을 추가하면,
|
||||
선택한 설비의 equipment_code가 자동으로 저장됩니다.
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
||||
{/* 설정 요약 */}
|
||||
@@ -343,6 +534,14 @@ export function ScreenSplitPanelConfigPanel({ config = {}, onChange }: ScreenSpl
|
||||
<span className="text-muted-foreground">크기 조절:</span>
|
||||
<span className="font-medium">{localConfig.resizable ? "가능" : "불가능"}</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-muted-foreground">데이터 매핑:</span>
|
||||
<span className="font-medium">
|
||||
{(localConfig.parentDataMapping || []).length > 0
|
||||
? `${localConfig.parentDataMapping.length}개 설정`
|
||||
: "미설정"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
Reference in New Issue
Block a user