196 lines
7.9 KiB
TypeScript
196 lines
7.9 KiB
TypeScript
'use client';
|
||
|
||
import React, { useState, useCallback } from 'react';
|
||
import { ChartConfig, QueryResult } from './types';
|
||
|
||
interface VehicleMapConfigPanelProps {
|
||
config?: ChartConfig;
|
||
queryResult?: QueryResult;
|
||
onConfigChange: (config: ChartConfig) => void;
|
||
}
|
||
|
||
/**
|
||
* 차량 위치 지도 설정 패널
|
||
* - 위도/경도 컬럼 매핑
|
||
* - 라벨/상태 컬럼 설정
|
||
*/
|
||
export function VehicleMapConfigPanel({ config, queryResult, onConfigChange }: VehicleMapConfigPanelProps) {
|
||
const [currentConfig, setCurrentConfig] = useState<ChartConfig>(config || {});
|
||
|
||
// 설정 업데이트
|
||
const updateConfig = useCallback((updates: Partial<ChartConfig>) => {
|
||
const newConfig = { ...currentConfig, ...updates };
|
||
setCurrentConfig(newConfig);
|
||
onConfigChange(newConfig);
|
||
}, [currentConfig, onConfigChange]);
|
||
|
||
// 사용 가능한 컬럼 목록
|
||
const availableColumns = queryResult?.columns || [];
|
||
const sampleData = queryResult?.rows?.[0] || {};
|
||
|
||
return (
|
||
<div className="space-y-3">
|
||
<h4 className="text-xs font-semibold text-gray-800">🗺️ 지도 설정</h4>
|
||
|
||
{/* 쿼리 결과가 없을 때 */}
|
||
{!queryResult && (
|
||
<div className="p-3 bg-yellow-50 border border-yellow-200 rounded-lg">
|
||
<div className="text-yellow-800 text-xs">
|
||
💡 먼저 SQL 쿼리를 실행하여 데이터를 가져온 후 지도를 설정할 수 있습니다.
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* 데이터 필드 매핑 */}
|
||
{queryResult && (
|
||
<>
|
||
{/* 지도 제목 */}
|
||
<div className="space-y-1.5">
|
||
<label className="block text-xs font-medium text-gray-700">지도 제목</label>
|
||
<input
|
||
type="text"
|
||
value={currentConfig.title || ''}
|
||
onChange={(e) => updateConfig({ title: e.target.value })}
|
||
placeholder="차량 위치 지도"
|
||
className="w-full px-2 py-1.5 border border-gray-300 rounded-lg text-xs"
|
||
/>
|
||
</div>
|
||
|
||
{/* 위도 컬럼 설정 */}
|
||
<div className="space-y-1.5">
|
||
<label className="block text-xs font-medium text-gray-700">
|
||
위도 컬럼 (Latitude)
|
||
<span className="text-red-500 ml-1">*</span>
|
||
</label>
|
||
<select
|
||
value={currentConfig.latitudeColumn || ''}
|
||
onChange={(e) => updateConfig({ latitudeColumn: e.target.value })}
|
||
className="w-full px-2 py-1.5 border border-gray-300 rounded-lg text-xs"
|
||
>
|
||
<option value="">선택하세요</option>
|
||
{availableColumns.map((col) => (
|
||
<option key={col} value={col}>
|
||
{col} {sampleData[col] && `(예: ${sampleData[col]})`}
|
||
</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
|
||
{/* 경도 컬럼 설정 */}
|
||
<div className="space-y-1.5">
|
||
<label className="block text-xs font-medium text-gray-700">
|
||
경도 컬럼 (Longitude)
|
||
<span className="text-red-500 ml-1">*</span>
|
||
</label>
|
||
<select
|
||
value={currentConfig.longitudeColumn || ''}
|
||
onChange={(e) => updateConfig({ longitudeColumn: e.target.value })}
|
||
className="w-full px-2 py-1.5 border border-gray-300 rounded-lg text-xs"
|
||
>
|
||
<option value="">선택하세요</option>
|
||
{availableColumns.map((col) => (
|
||
<option key={col} value={col}>
|
||
{col} {sampleData[col] && `(예: ${sampleData[col]})`}
|
||
</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
|
||
{/* 라벨 컬럼 (선택사항) */}
|
||
<div className="space-y-1.5">
|
||
<label className="block text-xs font-medium text-gray-700">
|
||
라벨 컬럼 (마커 표시명)
|
||
</label>
|
||
<select
|
||
value={currentConfig.labelColumn || ''}
|
||
onChange={(e) => updateConfig({ labelColumn: e.target.value })}
|
||
className="w-full px-2 py-1.5 border border-gray-300 rounded-lg text-xs"
|
||
>
|
||
<option value="">선택하세요 (선택사항)</option>
|
||
{availableColumns.map((col) => (
|
||
<option key={col} value={col}>
|
||
{col}
|
||
</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
|
||
{/* 상태 컬럼 (선택사항) */}
|
||
<div className="space-y-1.5">
|
||
<label className="block text-xs font-medium text-gray-700">
|
||
상태 컬럼 (마커 색상)
|
||
</label>
|
||
<select
|
||
value={currentConfig.statusColumn || ''}
|
||
onChange={(e) => updateConfig({ statusColumn: e.target.value })}
|
||
className="w-full px-2 py-1.5 border border-gray-300 rounded-lg text-xs"
|
||
>
|
||
<option value="">선택하세요 (선택사항)</option>
|
||
{availableColumns.map((col) => (
|
||
<option key={col} value={col}>
|
||
{col}
|
||
</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
|
||
{/* 날씨 정보 표시 옵션 */}
|
||
<div className="space-y-1.5">
|
||
<label className="flex items-center gap-2 text-xs font-medium text-gray-700 cursor-pointer">
|
||
<input
|
||
type="checkbox"
|
||
checked={currentConfig.showWeather || false}
|
||
onChange={(e) => updateConfig({ showWeather: e.target.checked })}
|
||
className="h-4 w-4 rounded border-gray-300 text-primary focus:ring-2 focus:ring-primary"
|
||
/>
|
||
<span>날씨 정보 표시</span>
|
||
</label>
|
||
<p className="text-xs text-gray-500 ml-6">
|
||
마커 팝업에 해당 위치의 날씨 정보를 함께 표시합니다
|
||
</p>
|
||
</div>
|
||
|
||
<div className="space-y-1.5">
|
||
<label className="flex items-center gap-2 text-xs font-medium text-gray-700 cursor-pointer">
|
||
<input
|
||
type="checkbox"
|
||
checked={currentConfig.showWeatherAlerts || false}
|
||
onChange={(e) => updateConfig({ showWeatherAlerts: e.target.checked })}
|
||
className="h-4 w-4 rounded border-gray-300 text-primary focus:ring-2 focus:ring-primary"
|
||
/>
|
||
<span>기상특보 영역 표시</span>
|
||
</label>
|
||
<p className="text-xs text-gray-500 ml-6">
|
||
현재 발효 중인 기상특보(주의보/경보)를 지도에 색상 영역으로 표시합니다
|
||
</p>
|
||
</div>
|
||
|
||
{/* 설정 미리보기 */}
|
||
<div className="p-3 bg-gray-50 rounded-lg">
|
||
<div className="text-xs font-medium text-gray-700 mb-2">📋 설정 미리보기</div>
|
||
<div className="text-xs text-muted-foreground space-y-1">
|
||
<div><strong>위도:</strong> {currentConfig.latitudeColumn || '미설정'}</div>
|
||
<div><strong>경도:</strong> {currentConfig.longitudeColumn || '미설정'}</div>
|
||
<div><strong>라벨:</strong> {currentConfig.labelColumn || '없음'}</div>
|
||
<div><strong>상태:</strong> {currentConfig.statusColumn || '없음'}</div>
|
||
<div><strong>날씨 표시:</strong> {currentConfig.showWeather ? '활성화' : '비활성화'}</div>
|
||
<div><strong>기상특보 표시:</strong> {currentConfig.showWeatherAlerts ? '활성화' : '비활성화'}</div>
|
||
<div><strong>데이터 개수:</strong> {queryResult.rows.length}개</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 필수 필드 확인 */}
|
||
{(!currentConfig.latitudeColumn || !currentConfig.longitudeColumn) && (
|
||
<div className="p-3 bg-red-50 border border-red-200 rounded-lg">
|
||
<div className="text-red-800 text-xs">
|
||
⚠️ 위도와 경도 컬럼을 반드시 선택해야 지도에 표시할 수 있습니다.
|
||
</div>
|
||
</div>
|
||
)}
|
||
</>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|
||
|