"use client"; import React, { useState, useCallback } from "react"; import { ChartDataSource, QueryResult, ChartConfig } from "./types"; import { ExternalDbConnectionAPI } from "@/lib/api/externalDbConnection"; import { dashboardApi } from "@/lib/api/dashboard"; import { Button } from "@/components/ui/button"; import { Textarea } from "@/components/ui/textarea"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Label } from "@/components/ui/label"; import { Card } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"; import { Play, Loader2, Database, Code, ChevronDown, ChevronRight } from "lucide-react"; import { applyQueryFilters } from "./utils/queryHelpers"; interface QueryEditorProps { dataSource?: ChartDataSource; onDataSourceChange: (dataSource: ChartDataSource) => void; onQueryTest?: (result: QueryResult) => void; } /** * SQL 쿼리 에디터 컴포넌트 * - SQL 쿼리 작성 및 편집 * - 쿼리 실행 및 결과 미리보기 * - 현재 DB / 외부 DB 분기 처리 */ export function QueryEditor({ dataSource, onDataSourceChange, onQueryTest }: QueryEditorProps) { const [query, setQuery] = useState(dataSource?.query || ""); const [isExecuting, setIsExecuting] = useState(false); const [queryResult, setQueryResult] = useState(null); const [error, setError] = useState(null); const [sampleQueryOpen, setSampleQueryOpen] = useState(false); // dataSource.query가 변경되면 query state 업데이트 (저장된 쿼리 불러오기) React.useEffect(() => { if (dataSource?.query) { setQuery(dataSource.query); } }, [dataSource?.query]); // 쿼리 실행 const executeQuery = useCallback(async () => { // console.log("🚀 executeQuery 호출됨!"); // console.log("📝 현재 쿼리:", query); // console.log("✅ query.trim():", query.trim()); if (!query.trim()) { setError("쿼리를 입력해주세요."); return; } // 외부 DB인 경우 커넥션 ID 확인 if (dataSource?.connectionType === "external" && !dataSource?.externalConnectionId) { setError("외부 DB 커넥션을 선택해주세요."); // console.log("❌ 쿼리가 비어있음!"); return; } setIsExecuting(true); setError(null); // console.log("🔄 쿼리 실행 시작..."); try { let apiResult: { columns: string[]; rows: any[]; rowCount: number }; // 현재 DB vs 외부 DB 분기 if (dataSource?.connectionType === "external" && dataSource?.externalConnectionId) { // 외부 DB 쿼리 실행 const result = await ExternalDbConnectionAPI.executeQuery( parseInt(dataSource.externalConnectionId), query.trim(), ); if (!result.success) { throw new Error(result.message || "외부 DB 쿼리 실행에 실패했습니다."); } // ExternalDbConnectionAPI의 응답을 통일된 형식으로 변환 apiResult = { columns: result.data?.[0] ? Object.keys(result.data[0]) : [], rows: result.data || [], rowCount: result.data?.length || 0, }; } else { // 현재 DB 쿼리 실행 apiResult = await dashboardApi.executeQuery(query.trim()); } // 결과를 QueryResult 형식으로 변환 const result: QueryResult = { columns: apiResult.columns, rows: apiResult.rows, totalRows: apiResult.rowCount, executionTime: 0, }; setQueryResult(result); onQueryTest?.(result); // 데이터 소스 업데이트 onDataSourceChange({ ...dataSource, type: "database", query: query.trim(), refreshInterval: dataSource?.refreshInterval ?? 0, lastExecuted: new Date().toISOString(), }); } catch (err) { const errorMessage = err instanceof Error ? err.message : "쿼리 실행 중 오류가 발생했습니다."; setError(errorMessage); } finally { setIsExecuting(false); } }, [query, dataSource, onDataSourceChange, onQueryTest]); // 샘플 쿼리 삽입 const insertSampleQuery = useCallback((sampleType: string) => { const samples = { users: `SELECT dept_name as 부서명, COUNT(*) as 회원수 FROM user_info WHERE dept_name IS NOT NULL GROUP BY dept_name ORDER BY 회원수 DESC`, dept: `SELECT dept_code as 부서코드, dept_name as 부서명, location_name as 위치, TO_CHAR(regdate, 'YYYY-MM-DD') as 등록일 FROM dept_info ORDER BY dept_code`, usersByDate: `SELECT DATE_TRUNC('month', regdate)::date as 월, COUNT(*) as 신규사용자수 FROM user_info WHERE regdate >= CURRENT_DATE - INTERVAL '12 months' GROUP BY DATE_TRUNC('month', regdate) ORDER BY 월`, usersByPosition: `SELECT position_name as 직급, COUNT(*) as 인원수 FROM user_info WHERE position_name IS NOT NULL GROUP BY position_name ORDER BY 인원수 DESC`, deptHierarchy: `SELECT COALESCE(parent_dept_code, '최상위') as 상위부서코드, COUNT(*) as 하위부서수 FROM dept_info GROUP BY parent_dept_code ORDER BY 하위부서수 DESC`, }; setQuery(samples[sampleType as keyof typeof samples] || ""); }, []); return (
{/* 쿼리 에디터 헤더 */}

SQL 쿼리 에디터

{/* 샘플 쿼리 아코디언 */} {sampleQueryOpen ? : } 샘플 쿼리
{/* SQL 쿼리 입력 영역 */}