192 lines
7.4 KiB
TypeScript
192 lines
7.4 KiB
TypeScript
"use client";
|
|
|
|
import React, { useState, useEffect } from "react";
|
|
import { ChartDataSource } from "../types";
|
|
import { ExternalDbConnectionAPI, ExternalDbConnection } from "@/lib/api/externalDbConnection";
|
|
import { Card } from "@/components/ui/card";
|
|
import { Label } from "@/components/ui/label";
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
|
import { Button } from "@/components/ui/button";
|
|
import { ExternalLink, Database, Server } from "lucide-react";
|
|
|
|
interface DatabaseConfigProps {
|
|
dataSource: ChartDataSource;
|
|
onChange: (updates: Partial<ChartDataSource>) => void;
|
|
}
|
|
|
|
/**
|
|
* 데이터베이스 설정 컴포넌트
|
|
* - 현재 DB / 외부 DB 선택
|
|
* - 외부 커넥션 목록 불러오기
|
|
*/
|
|
export function DatabaseConfig({ dataSource, onChange }: DatabaseConfigProps) {
|
|
const [connections, setConnections] = useState<ExternalDbConnection[]>([]);
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
// 외부 커넥션 목록 불러오기
|
|
useEffect(() => {
|
|
if (dataSource.connectionType === "external") {
|
|
loadExternalConnections();
|
|
}
|
|
}, [dataSource.connectionType]);
|
|
|
|
const loadExternalConnections = async () => {
|
|
setLoading(true);
|
|
setError(null);
|
|
try {
|
|
const activeConnections = await ExternalDbConnectionAPI.getConnections({ is_active: "Y" });
|
|
setConnections(activeConnections);
|
|
} catch (err) {
|
|
const errorMessage = err instanceof Error ? err.message : "알 수 없는 오류가 발생했습니다";
|
|
setError(errorMessage);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
// 현재 선택된 커넥션 찾기
|
|
const selectedConnection = connections.find((conn) => String(conn.id) === dataSource.externalConnectionId);
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
<div>
|
|
<h3 className="text-lg font-semibold text-gray-800">2단계: 데이터베이스 설정</h3>
|
|
<p className="mt-1 text-sm text-gray-600">데이터를 조회할 데이터베이스를 선택하세요</p>
|
|
</div>
|
|
|
|
{/* 현재 DB vs 외부 DB 선택 */}
|
|
<Card className="p-4">
|
|
<Label className="mb-3 block text-sm font-medium text-gray-700">데이터베이스 선택</Label>
|
|
<div className="grid grid-cols-2 gap-3">
|
|
<Button
|
|
variant={dataSource.connectionType === "current" ? "default" : "outline"}
|
|
className="h-auto justify-start py-3"
|
|
onClick={() => {
|
|
onChange({ connectionType: "current", externalConnectionId: undefined });
|
|
}}
|
|
>
|
|
<Database className="mr-2 h-4 w-4" />
|
|
<div className="text-left">
|
|
<div className="font-medium">현재 데이터베이스</div>
|
|
<div className="text-xs opacity-80">애플리케이션 기본 DB</div>
|
|
</div>
|
|
</Button>
|
|
|
|
<Button
|
|
variant={dataSource.connectionType === "external" ? "default" : "outline"}
|
|
className="h-auto justify-start py-3"
|
|
onClick={() => {
|
|
onChange({ connectionType: "external" });
|
|
}}
|
|
>
|
|
<Server className="mr-2 h-4 w-4" />
|
|
<div className="text-left">
|
|
<div className="font-medium">외부 데이터베이스</div>
|
|
<div className="text-xs opacity-80">등록된 외부 커넥션</div>
|
|
</div>
|
|
</Button>
|
|
</div>
|
|
</Card>
|
|
|
|
{/* 외부 DB 선택 시 커넥션 목록 */}
|
|
{dataSource.connectionType === "external" && (
|
|
<Card className="space-y-4 p-4">
|
|
<div className="flex items-center justify-between">
|
|
<Label className="text-sm font-medium text-gray-700">외부 커넥션 선택</Label>
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={() => {
|
|
window.open("/admin/external-connections", "_blank");
|
|
}}
|
|
className="text-xs"
|
|
>
|
|
<ExternalLink className="mr-1 h-3 w-3" />
|
|
커넥션 관리
|
|
</Button>
|
|
</div>
|
|
|
|
{loading && (
|
|
<div className="flex items-center justify-center py-4">
|
|
<div className="h-5 w-5 animate-spin rounded-full border-2 border-gray-300 border-t-blue-600" />
|
|
<span className="ml-2 text-sm text-gray-600">커넥션 목록 불러오는 중...</span>
|
|
</div>
|
|
)}
|
|
|
|
{error && (
|
|
<div className="rounded-lg border border-red-200 bg-red-50 p-3">
|
|
<div className="text-sm text-red-800">❌ {error}</div>
|
|
<Button variant="ghost" size="sm" onClick={loadExternalConnections} className="mt-2 text-xs">
|
|
다시 시도
|
|
</Button>
|
|
</div>
|
|
)}
|
|
|
|
{!loading && !error && connections.length === 0 && (
|
|
<div className="rounded-lg border border-yellow-200 bg-yellow-50 p-4 text-center">
|
|
<div className="mb-2 text-sm text-yellow-800">등록된 외부 커넥션이 없습니다</div>
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={() => {
|
|
window.open("/admin/external-connections", "_blank");
|
|
}}
|
|
>
|
|
<ExternalLink className="mr-1 h-3 w-3" />
|
|
커넥션 등록하기
|
|
</Button>
|
|
</div>
|
|
)}
|
|
|
|
{!loading && !error && connections.length > 0 && (
|
|
<>
|
|
<Select
|
|
value={dataSource.externalConnectionId || undefined}
|
|
onValueChange={(value) => {
|
|
onChange({ externalConnectionId: value });
|
|
}}
|
|
>
|
|
<SelectTrigger className="w-full">
|
|
<SelectValue placeholder="커넥션을 선택하세요" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{connections.map((conn) => (
|
|
<SelectItem key={conn.id} value={String(conn.id)}>
|
|
<div className="flex items-center gap-2">
|
|
<span className="font-medium">{conn.connection_name}</span>
|
|
<span className="text-xs text-gray-500">({conn.db_type.toUpperCase()})</span>
|
|
</div>
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
|
|
{selectedConnection && (
|
|
<div className="rounded-lg border border-gray-200 bg-gray-50 p-3">
|
|
<div className="space-y-1 text-xs text-gray-600">
|
|
<div>
|
|
<span className="font-medium">커넥션명:</span> {selectedConnection.connection_name}
|
|
</div>
|
|
<div>
|
|
<span className="font-medium">타입:</span> {selectedConnection.db_type.toUpperCase()}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</>
|
|
)}
|
|
</Card>
|
|
)}
|
|
|
|
{/* 다음 단계 안내 */}
|
|
{(dataSource.connectionType === "current" ||
|
|
(dataSource.connectionType === "external" && dataSource.externalConnectionId)) && (
|
|
<div className="rounded-lg border border-blue-200 bg-blue-50 p-3">
|
|
<div className="text-sm text-blue-800">✅ 데이터베이스가 선택되었습니다. 아래에서 SQL 쿼리를 작성하세요.</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|