176 lines
6.6 KiB
TypeScript
176 lines
6.6 KiB
TypeScript
"use client";
|
|
|
|
import React, { useState, useEffect } from "react";
|
|
import { useRouter } from "next/navigation";
|
|
import { ChartDataSource } from "../types";
|
|
import { ExternalDbConnectionAPI, ExternalDbConnection } from "@/lib/api/externalDbConnection";
|
|
import { Label } from "@/components/ui/label";
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
|
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 router = useRouter();
|
|
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-3">
|
|
{/* 현재 DB vs 외부 DB 선택 */}
|
|
<div>
|
|
<Label className="text-foreground mb-2 block text-xs font-medium">데이터베이스 선택</Label>
|
|
<div className="flex gap-2">
|
|
<button
|
|
onClick={() => {
|
|
onChange({ connectionType: "current", externalConnectionId: undefined });
|
|
}}
|
|
className={`flex flex-1 items-center gap-1.5 rounded border px-2 py-1.5 text-xs transition-colors ${
|
|
dataSource.connectionType === "current"
|
|
? "bg-primary border-primary text-white"
|
|
: "border-border bg-background hover:bg-muted"
|
|
}`}
|
|
>
|
|
<Database className="h-3 w-3" />
|
|
현재 DB
|
|
</button>
|
|
|
|
<button
|
|
onClick={() => {
|
|
onChange({ connectionType: "external" });
|
|
}}
|
|
className={`flex flex-1 items-center gap-1.5 rounded border px-2 py-1.5 text-xs transition-colors ${
|
|
dataSource.connectionType === "external"
|
|
? "bg-primary border-primary text-white"
|
|
: "border-border bg-background hover:bg-muted"
|
|
}`}
|
|
>
|
|
<Server className="h-3 w-3" />
|
|
외부 DB
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* 외부 DB 선택 시 커넥션 목록 */}
|
|
{dataSource.connectionType === "external" && (
|
|
<div className="space-y-2">
|
|
<div className="flex items-center justify-between">
|
|
<Label className="text-foreground text-xs font-medium">외부 커넥션</Label>
|
|
<button
|
|
onClick={() => {
|
|
router.push("/admin/automaticMng/exconList");
|
|
}}
|
|
className="text-primary hover:text-primary flex items-center gap-1 text-[11px] transition-colors"
|
|
>
|
|
<ExternalLink className="h-3 w-3" />
|
|
커넥션 관리
|
|
</button>
|
|
</div>
|
|
|
|
{loading && (
|
|
<div className="flex items-center justify-center py-3">
|
|
<div className="border-border h-4 w-4 animate-spin rounded-full border-2 border-t-blue-600" />
|
|
<span className="text-foreground ml-2 text-xs">로딩 중...</span>
|
|
</div>
|
|
)}
|
|
|
|
{error && (
|
|
<div className="bg-destructive/10 rounded px-2 py-1.5">
|
|
<div className="text-destructive text-xs">{error}</div>
|
|
<button
|
|
onClick={loadExternalConnections}
|
|
className="text-destructive mt-1 text-[11px] underline hover:no-underline"
|
|
>
|
|
다시 시도
|
|
</button>
|
|
</div>
|
|
)}
|
|
|
|
{!loading && !error && connections.length === 0 && (
|
|
<div className="bg-warning/10 rounded px-2 py-2 text-center">
|
|
<div className="text-warning mb-1 text-xs">등록된 커넥션이 없습니다</div>
|
|
<button
|
|
onClick={() => {
|
|
router.push("/admin/automaticMng/exconList");
|
|
}}
|
|
className="text-warning text-[11px] underline hover:no-underline"
|
|
>
|
|
커넥션 등록하기
|
|
</button>
|
|
</div>
|
|
)}
|
|
|
|
{!loading && !error && connections.length > 0 && (
|
|
<>
|
|
<Select
|
|
value={dataSource.externalConnectionId || undefined}
|
|
onValueChange={(value) => {
|
|
onChange({ externalConnectionId: value });
|
|
}}
|
|
>
|
|
<SelectTrigger className="h-8 w-full text-xs">
|
|
<SelectValue placeholder="커넥션 선택" />
|
|
</SelectTrigger>
|
|
<SelectContent className="z-[9999]">
|
|
{connections.map((conn) => (
|
|
<SelectItem key={conn.id} value={String(conn.id)} className="text-xs">
|
|
<div className="flex items-center gap-1.5">
|
|
<span className="font-medium">{conn.connection_name}</span>
|
|
<span className="text-muted-foreground text-[10px]">({conn.db_type.toUpperCase()})</span>
|
|
</div>
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
|
|
{selectedConnection && (
|
|
<div className="bg-muted text-foreground space-y-0.5 rounded px-2 py-1.5 text-[11px]">
|
|
<div>
|
|
<span className="font-medium">커넥션:</span> {selectedConnection.connection_name}
|
|
</div>
|
|
<div>
|
|
<span className="font-medium">타입:</span> {selectedConnection.db_type.toUpperCase()}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|