'use client'; import React, { useState, useCallback, useEffect } from 'react'; import { ChartConfig, QueryResult, ChartDataSource } from './types'; import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; import { Label } from '@/components/ui/label'; import { Plus, X } from 'lucide-react'; import { ExternalDbConnectionAPI, ExternalApiConnection } from '@/lib/api/externalDbConnection'; interface MapTestConfigPanelProps { config?: ChartConfig; queryResult?: QueryResult; onConfigChange: (config: ChartConfig) => void; } /** * 지도 테스트 위젯 설정 패널 * - 타일맵 URL 설정 (VWorld, OpenStreetMap 등) * - 위도/경도 컬럼 매핑 * - 라벨/상태 컬럼 설정 */ export function MapTestConfigPanel({ config, queryResult, onConfigChange }: MapTestConfigPanelProps) { const [currentConfig, setCurrentConfig] = useState(config || {}); const [connections, setConnections] = useState([]); const [tileMapSources, setTileMapSources] = useState>([ { id: `tilemap_${Date.now()}`, url: '' } ]); // config prop 변경 시 currentConfig 동기화 useEffect(() => { if (config) { setCurrentConfig(config); console.log('🔄 config 업데이트:', config); } }, [config]); // 외부 API 커넥션 목록 불러오기 (REST API만) useEffect(() => { const loadApiConnections = async () => { try { const apiConnections = await ExternalDbConnectionAPI.getApiConnections({ is_active: 'Y' }); setConnections(apiConnections); console.log('✅ REST API 커넥션 로드 완료:', apiConnections); console.log(`📊 총 ${apiConnections.length}개의 REST API 커넥션`); } catch (error) { console.error('❌ REST API 커넥션 로드 실패:', error); } }; loadApiConnections(); }, []); // 타일맵 URL을 템플릿 형식으로 변환 (10/856/375.png → {z}/{y}/{x}.png) const convertToTileTemplate = (url: string): string => { // 이미 템플릿 형식이면 그대로 반환 if (url.includes('{z}') && url.includes('{y}') && url.includes('{x}')) { return url; } // 특정 타일 URL 패턴 감지: /숫자/숫자/숫자.png const tilePattern = /\/(\d+)\/(\d+)\/(\d+)\.(png|jpg|jpeg)$/i; const match = url.match(tilePattern); if (match) { // /10/856/375.png → /{z}/{y}/{x}.png const convertedUrl = url.replace(tilePattern, '/{z}/{y}/{x}.$4'); console.log('🔄 타일 URL 자동 변환:', url, '→', convertedUrl); return convertedUrl; } return url; }; // 설정 업데이트 const updateConfig = useCallback((updates: Partial) => { // tileMapUrl이 업데이트되면 자동으로 템플릿 형식으로 변환 if (updates.tileMapUrl) { updates.tileMapUrl = convertToTileTemplate(updates.tileMapUrl); } const newConfig = { ...currentConfig, ...updates }; setCurrentConfig(newConfig); onConfigChange(newConfig); }, [currentConfig, onConfigChange]); // 타일맵 소스 추가 const addTileMapSource = () => { setTileMapSources([...tileMapSources, { id: `tilemap_${Date.now()}`, url: '' }]); }; // 타일맵 소스 제거 const removeTileMapSource = (id: string) => { if (tileMapSources.length === 1) return; // 최소 1개는 유지 setTileMapSources(tileMapSources.filter(s => s.id !== id)); }; // 타일맵 소스 업데이트 const updateTileMapSource = (id: string, url: string) => { setTileMapSources(tileMapSources.map(s => s.id === id ? { ...s, url } : s)); // 첫 번째 타일맵 URL을 config에 저장 const firstUrl = id === tileMapSources[0].id ? url : tileMapSources[0].url; updateConfig({ tileMapUrl: firstUrl }); }; // 외부 커넥션에서 URL 가져오기 const loadFromConnection = (sourceId: string, connectionId: string) => { const connection = connections.find(c => c.id?.toString() === connectionId); if (connection) { console.log('🔗 선택된 커넥션:', connection.connection_name, '→', connection.base_url); updateTileMapSource(sourceId, connection.base_url); } }; // 사용 가능한 컬럼 목록 const availableColumns = queryResult?.columns || []; const sampleData = queryResult?.rows?.[0] || {}; // 기상특보 데이터인지 감지 (reg_ko, wrn 컬럼이 있으면 기상특보) const isWeatherAlertData = availableColumns.includes('reg_ko') && availableColumns.includes('wrn'); return (
{/* 타일맵 URL 설정 (외부 커넥션 또는 직접 입력) */}
{/* 외부 커넥션 선택 */} {/* 타일맵 URL 직접 입력 */} updateConfig({ tileMapUrl: e.target.value })} placeholder="https://api.vworld.kr/req/wmts/1.0.0/{API_KEY}/Base/{z}/{y}/{x}.png" className="h-8 text-xs" />

💡 {'{z}/{y}/{x}'}는 그대로 입력하세요 (지도 라이브러리가 자동 치환)

{/* 타일맵 소스 목록 */} {/*
{tileMapSources.map((source, index) => (
updateTileMapSource(source.id, e.target.value)} placeholder="https://api.vworld.kr/req/wmts/1.0.0/{API_KEY}/Base/{z}/{y}/{x}.png" className="h-8 flex-1 text-xs" /> {tileMapSources.length > 1 && ( )}
))}

💡 {'{z}/{y}/{x}'}는 그대로 입력하세요 (지도 라이브러리가 자동 치환)

*/} {/* 지도 제목 */} {/*
updateConfig({ title: e.target.value })} placeholder="위치 지도" className="h-10 text-xs" />
*/} {/* 구분선 */} {/*
📍 마커 데이터 설정 (선택사항)

데이터 소스 탭에서 API 또는 데이터베이스를 연결하면 마커를 표시할 수 있습니다.

*/} {/* 쿼리 결과가 없을 때 */} {/* {!queryResult && (
💡 데이터 소스를 연결하고 쿼리를 실행하면 마커 설정이 가능합니다.
)} */} {/* 데이터 필드 매핑 */} {queryResult && !isWeatherAlertData && ( <> {/* 위도 컬럼 설정 */}
{/* 경도 컬럼 설정 */}
{/* 라벨 컬럼 (선택사항) */}
{/* 상태 컬럼 (선택사항) */}
)} {/* 기상특보 데이터 안내 */} {queryResult && isWeatherAlertData && (
🚨 기상특보 데이터가 감지되었습니다. 지역명(reg_ko)을 기준으로 자동으로 영역이 표시됩니다.
)} {queryResult && ( <> {/* 날씨 정보 표시 옵션 */}

마커 팝업에 해당 위치의 날씨 정보를 함께 표시합니다

현재 발효 중인 기상특보(주의보/경보)를 지도에 색상 영역으로 표시합니다

{/* 설정 미리보기 */}
📋 설정 미리보기
타일맵: {currentConfig.tileMapUrl ? '✅ 설정됨' : '❌ 미설정'}
위도: {currentConfig.latitudeColumn || '미설정'}
경도: {currentConfig.longitudeColumn || '미설정'}
라벨: {currentConfig.labelColumn || '없음'}
상태: {currentConfig.statusColumn || '없음'}
날씨 표시: {currentConfig.showWeather ? '활성화' : '비활성화'}
기상특보 표시: {currentConfig.showWeatherAlerts ? '활성화' : '비활성화'}
데이터 개수: {queryResult.rows.length}개
)} {/* 필수 필드 확인 */} {/* {!currentConfig.tileMapUrl && (
⚠️ 타일맵 URL을 입력해야 지도가 표시됩니다.
)} */}
); }