diff --git a/frontend/components/admin/dashboard/WidgetConfigSidebar.tsx b/frontend/components/admin/dashboard/WidgetConfigSidebar.tsx index 710abbe6..e79c6446 100644 --- a/frontend/components/admin/dashboard/WidgetConfigSidebar.tsx +++ b/frontend/components/admin/dashboard/WidgetConfigSidebar.tsx @@ -520,7 +520,39 @@ export function WidgetConfigSidebar({ element, isOpen, onClose, onApply }: Widge )} {/* 지도 설정 */} - {element.subtype === "map-summary-v2" && } + {element.subtype === "map-summary-v2" && ( + { + setElement((prev) => + prev + ? { + ...prev, + chartConfig: { + ...prev.chartConfig, + refreshInterval: interval, + }, + } + : prev + ); + }} + onMarkerTypeChange={(type) => { + setElement((prev) => + prev + ? { + ...prev, + chartConfig: { + ...prev.chartConfig, + markerType: type, + }, + } + : prev + ); + }} + /> + )} {/* 리스크 알림 설정 */} {element.subtype === "risk-alert-v2" && } diff --git a/frontend/components/admin/dashboard/data-sources/MultiApiConfig.tsx b/frontend/components/admin/dashboard/data-sources/MultiApiConfig.tsx index bbbf7d4d..f16e8436 100644 --- a/frontend/components/admin/dashboard/data-sources/MultiApiConfig.tsx +++ b/frontend/components/admin/dashboard/data-sources/MultiApiConfig.tsx @@ -530,31 +530,50 @@ export default function MultiApiConfig({ dataSource, onChange, onTestResult }: M ))} - {/* 자동 새로고침 설정 */} + {/* 마커 polling 간격 설정 (MapTestWidgetV2 전용) */} - - 자동 새로고침 간격 + + 마커 새로고침 간격 onChange({ refreshInterval: Number(value) })} + value={(dataSource.refreshInterval ?? 5).toString()} + onValueChange={(value) => onChange({ refreshInterval: parseInt(value) })} > - - + + - 새로고침 안 함 - 10초마다 - 30초마다 - 1분마다 - 5분마다 - 10분마다 - 30분마다 - 1시간마다 + 없음 + 5초 + 10초 + 30초 + 1분 - 설정한 간격마다 자동으로 데이터를 다시 불러옵니다 + 마커 데이터를 자동으로 갱신하는 주기를 설정합니다 + + + + {/* 마커 종류 선택 (MapTestWidgetV2 전용) */} + + + 마커 종류 + + onChange({ markerType: value })} + > + + + + + 동그라미 + 화살표 + + + + 지도에 표시할 마커의 모양을 선택합니다 diff --git a/frontend/components/admin/dashboard/types.ts b/frontend/components/admin/dashboard/types.ts index 8b8ff2b4..61fa60ae 100644 --- a/frontend/components/admin/dashboard/types.ts +++ b/frontend/components/admin/dashboard/types.ts @@ -52,7 +52,7 @@ export type ElementSubtype = | "yard-management-3d" // 야드 관리 3D 위젯 | "work-history" // 작업 이력 위젯 | "transport-stats"; // 커스텀 통계 카드 위젯 - // | "custom-metric"; // (구버전 - 주석 처리: 2025-10-28, custom-metric-v2로 대체) +// | "custom-metric"; // (구버전 - 주석 처리: 2025-10-28, custom-metric-v2로 대체) // 차트 분류 export type ChartCategory = "axis-based" | "circular"; @@ -164,6 +164,7 @@ export interface ChartDataSource { markerColor?: string; // 마커 색상 (예: "#ff0000") polygonColor?: string; // 폴리곤 색상 (예: "#0000ff") polygonOpacity?: number; // 폴리곤 투명도 (0.0 ~ 1.0, 기본값: 0.5) + markerType?: string; // 마커 종류 (circle, arrow) // 컬럼 매핑 (다중 데이터 소스 통합용) columnMapping?: Record; // { 원본컬럼: 표시이름 } (예: { "name": "product" }) diff --git a/frontend/components/admin/dashboard/widget-sections/MapConfigSection.tsx b/frontend/components/admin/dashboard/widget-sections/MapConfigSection.tsx index ae75f7cb..3ed5fe24 100644 --- a/frontend/components/admin/dashboard/widget-sections/MapConfigSection.tsx +++ b/frontend/components/admin/dashboard/widget-sections/MapConfigSection.tsx @@ -3,20 +3,30 @@ import React from "react"; import { QueryResult } from "../types"; import { Label } from "@/components/ui/label"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { AlertCircle } from "lucide-react"; interface MapConfigSectionProps { queryResult: QueryResult | null; + refreshInterval?: number; + markerType?: string; + onRefreshIntervalChange?: (interval: number) => void; + onMarkerTypeChange?: (type: string) => void; } /** * 지도 위젯 설정 섹션 - * - 위도/경도 매핑 - * - * TODO: 상세 설정 UI 추가 필요 + * - 자동 새로고침 간격 설정 + * - 마커 종류 선택 */ -export function MapConfigSection({ queryResult }: MapConfigSectionProps) { +export function MapConfigSection({ + queryResult, + refreshInterval = 5, + markerType = "circle", + onRefreshIntervalChange, + onMarkerTypeChange +}: MapConfigSectionProps) { // 쿼리 결과가 없으면 안내 메시지 표시 if (!queryResult || !queryResult.columns || queryResult.columns.length === 0) { return ( @@ -34,13 +44,56 @@ export function MapConfigSection({ queryResult }: MapConfigSectionProps) { return ( - 지도 설정 - - - - 지도 상세 설정 UI는 추후 추가 예정입니다. - - + 지도 설정 + + + {/* 자동 새로고침 간격 */} + + + 자동 새로고침 간격 + + onRefreshIntervalChange?.(parseInt(value))} + > + + + + + 없음 + 5초 + 10초 + 30초 + 1분 + + + + 마커 데이터를 자동으로 갱신하는 주기를 설정합니다 + + + + {/* 마커 종류 선택 */} + + + 마커 종류 + + onMarkerTypeChange?.(value)} + > + + + + + 동그라미 + 화살표 + + + + 지도에 표시할 마커의 모양을 선택합니다 + + + ); } diff --git a/frontend/components/dashboard/widgets/MapTestWidgetV2.tsx b/frontend/components/dashboard/widgets/MapTestWidgetV2.tsx index 51419821..834275b3 100644 --- a/frontend/components/dashboard/widgets/MapTestWidgetV2.tsx +++ b/frontend/components/dashboard/widgets/MapTestWidgetV2.tsx @@ -65,10 +65,6 @@ interface PolygonData { } export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) { - console.log("🗺️ MapTestWidgetV2 컴포넌트 마운트/렌더링"); - console.log("📦 element:", element); - console.log("📊 dataSources:", element?.dataSources); - const [markers, setMarkers] = useState([]); const [prevMarkers, setPrevMarkers] = useState([]); // 이전 마커 위치 저장 const [polygons, setPolygons] = useState([]); @@ -911,31 +907,29 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) { // 초기 로드 및 자동 새로고침 (마커 데이터만 polling) useEffect(() => { - console.log("🔄 지도 위젯 초기화 useEffect 실행됨!"); - console.log("📊 dataSources 상태:", dataSources); - if (!dataSources || dataSources.length === 0) { - console.log("⚠️ dataSources가 없거나 비어있음 - polling 시작 안함"); setMarkers([]); setPolygons([]); return; } // 즉시 첫 로드 (마커 데이터) - console.log("📡 초기 마커 데이터 로드"); loadMultipleDataSources(); - // 5초마다 마커 데이터만 자동 새로고침 (지도 타일은 안 건드림) - const refreshInterval = 5; - console.log(`⏱️ 마커 polling 시작: ${refreshInterval}초마다`); + // 첫 번째 데이터 소스의 새로고침 간격 사용 (초) + const firstDataSource = dataSources[0]; + const refreshInterval = firstDataSource?.refreshInterval ?? 5; + + // 0이면 자동 새로고침 비활성화 + if (refreshInterval === 0) { + return; + } const intervalId = setInterval(() => { - console.log("🔄 마커 자동 새로고침 실행"); loadMultipleDataSources(); }, refreshInterval * 1000); return () => { - console.log("⏹️ 마커 polling 정리"); clearInterval(intervalId); }; // eslint-disable-next-line react-hooks/exhaustive-deps @@ -1124,59 +1118,103 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) { ))} - {/* 마커 렌더링 (화살표 모양) */} + {/* 마커 렌더링 */} {markers.map((marker) => { - // 화살표 아이콘 생성 (진행 방향으로 회전) - let arrowIcon: any; + // 첫 번째 데이터 소스의 마커 종류 가져오기 + const firstDataSource = dataSources?.[0]; + const markerType = firstDataSource?.markerType || "circle"; + + let markerIcon: any; if (typeof window !== "undefined") { const L = require("leaflet"); const heading = marker.heading || 0; - arrowIcon = L.divIcon({ - className: "custom-arrow-marker", - html: ` - - - - - - - - - - - `, - iconSize: [40, 40], - iconAnchor: [20, 20], - }); + + if (markerType === "arrow") { + // 화살표 마커 + markerIcon = L.divIcon({ + className: "custom-arrow-marker", + html: ` + + + + + + + + + + + `, + iconSize: [40, 40], + iconAnchor: [20, 20], + }); + } else { + // 동그라미 마커 (기본) + markerIcon = L.divIcon({ + className: "custom-circle-marker", + html: ` + + + + + + + + + `, + iconSize: [32, 32], + iconAnchor: [16, 16], + }); + } } return ( - + {/* 제목 */}
- 설정한 간격마다 자동으로 데이터를 다시 불러옵니다 + 마커 데이터를 자동으로 갱신하는 주기를 설정합니다 +
+ 지도에 표시할 마커의 모양을 선택합니다
+ 마커 데이터를 자동으로 갱신하는 주기를 설정합니다 +
+ 지도에 표시할 마커의 모양을 선택합니다 +