restapi 여러개 띄우는거 작업 가능하게 하는거 진행중
This commit is contained in:
@@ -5,8 +5,10 @@ import { DashboardElement, ChartDataSource, ChartConfig, QueryResult } from "./t
|
||||
import { QueryEditor } from "./QueryEditor";
|
||||
import { ChartConfigPanel } from "./ChartConfigPanel";
|
||||
import { VehicleMapConfigPanel } from "./VehicleMapConfigPanel";
|
||||
import { MapTestConfigPanel } from "./MapTestConfigPanel";
|
||||
import { DatabaseConfig } from "./data-sources/DatabaseConfig";
|
||||
import { ApiConfig } from "./data-sources/ApiConfig";
|
||||
import MultiDataSourceConfig from "./data-sources/MultiDataSourceConfig";
|
||||
import { ListWidgetConfigSidebar } from "./widgets/ListWidgetConfigSidebar";
|
||||
import { YardWidgetConfigSidebar } from "./widgets/YardWidgetConfigSidebar";
|
||||
import { X } from "lucide-react";
|
||||
@@ -33,6 +35,7 @@ export function ElementConfigSidebar({ element, isOpen, onClose, onApply }: Elem
|
||||
connectionType: "current",
|
||||
refreshInterval: 0,
|
||||
});
|
||||
const [dataSources, setDataSources] = useState<ChartDataSource[]>([]);
|
||||
const [chartConfig, setChartConfig] = useState<ChartConfig>({});
|
||||
const [queryResult, setQueryResult] = useState<QueryResult | null>(null);
|
||||
const [customTitle, setCustomTitle] = useState<string>("");
|
||||
@@ -42,6 +45,8 @@ export function ElementConfigSidebar({ element, isOpen, onClose, onApply }: Elem
|
||||
useEffect(() => {
|
||||
if (isOpen && element) {
|
||||
setDataSource(element.dataSource || { type: "database", connectionType: "current", refreshInterval: 0 });
|
||||
// dataSources는 element.dataSources 또는 chartConfig.dataSources에서 로드
|
||||
setDataSources(element.dataSources || element.chartConfig?.dataSources || []);
|
||||
setChartConfig(element.chartConfig || {});
|
||||
setQueryResult(null);
|
||||
setCustomTitle(element.customTitle || "");
|
||||
@@ -89,9 +94,23 @@ export function ElementConfigSidebar({ element, isOpen, onClose, onApply }: Elem
|
||||
}, []);
|
||||
|
||||
// 차트 설정 변경 처리
|
||||
const handleChartConfigChange = useCallback((newConfig: ChartConfig) => {
|
||||
setChartConfig(newConfig);
|
||||
}, []);
|
||||
const handleChartConfigChange = useCallback(
|
||||
(newConfig: ChartConfig) => {
|
||||
setChartConfig(newConfig);
|
||||
|
||||
// 🎯 실시간 미리보기: 즉시 부모에게 전달 (map-test 위젯용)
|
||||
if (element && element.subtype === "map-test" && newConfig.tileMapUrl) {
|
||||
onApply({
|
||||
...element,
|
||||
chartConfig: newConfig,
|
||||
dataSource: dataSource,
|
||||
customTitle: customTitle,
|
||||
showHeader: showHeader,
|
||||
});
|
||||
}
|
||||
},
|
||||
[element, dataSource, customTitle, showHeader, onApply],
|
||||
);
|
||||
|
||||
// 쿼리 테스트 결과 처리
|
||||
const handleQueryTest = useCallback((result: QueryResult) => {
|
||||
@@ -103,17 +122,27 @@ export function ElementConfigSidebar({ element, isOpen, onClose, onApply }: Elem
|
||||
const handleApply = useCallback(() => {
|
||||
if (!element) return;
|
||||
|
||||
console.log("🔧 적용 버튼 클릭 - dataSource:", dataSource);
|
||||
console.log("🔧 적용 버튼 클릭 - dataSources:", element.dataSources);
|
||||
console.log("🔧 적용 버튼 클릭 - chartConfig:", chartConfig);
|
||||
|
||||
// 다중 데이터 소스 위젯 체크
|
||||
const isMultiDS = element.subtype === "map-test-v2" || element.subtype === "chart-test";
|
||||
|
||||
const updatedElement: DashboardElement = {
|
||||
...element,
|
||||
dataSource,
|
||||
chartConfig,
|
||||
// 다중 데이터 소스 위젯은 dataSources를 chartConfig에 저장
|
||||
chartConfig: isMultiDS ? { ...chartConfig, dataSources } : chartConfig,
|
||||
dataSources: isMultiDS ? dataSources : undefined, // 프론트엔드 호환성
|
||||
dataSource: isMultiDS ? undefined : dataSource,
|
||||
customTitle: customTitle.trim() || undefined,
|
||||
showHeader,
|
||||
};
|
||||
|
||||
console.log("🔧 적용할 요소:", updatedElement);
|
||||
onApply(updatedElement);
|
||||
// 사이드바는 열린 채로 유지 (연속 수정 가능)
|
||||
}, [element, dataSource, chartConfig, customTitle, showHeader, onApply]);
|
||||
}, [element, dataSource, dataSources, chartConfig, customTitle, showHeader, onApply]);
|
||||
|
||||
// 요소가 없으면 렌더링하지 않음
|
||||
if (!element) return null;
|
||||
@@ -184,13 +213,17 @@ export function ElementConfigSidebar({ element, isOpen, onClose, onApply }: Elem
|
||||
element.subtype === "weather" || element.subtype === "exchange" || element.subtype === "calculator";
|
||||
|
||||
// 지도 위젯 (위도/경도 매핑 필요)
|
||||
const isMapWidget = element.subtype === "vehicle-map" || element.subtype === "map-summary";
|
||||
const isMapWidget =
|
||||
element.subtype === "vehicle-map" || element.subtype === "map-summary" || element.subtype === "map-test";
|
||||
|
||||
// 헤더 전용 위젯
|
||||
const isHeaderOnlyWidget =
|
||||
element.type === "widget" &&
|
||||
(element.subtype === "clock" || element.subtype === "calendar" || isSelfContainedWidget);
|
||||
|
||||
// 다중 데이터 소스 테스트 위젯
|
||||
const isMultiDataSourceWidget = element.subtype === "map-test-v2" || element.subtype === "chart-test";
|
||||
|
||||
// 저장 가능 여부 확인
|
||||
const isPieChart = element.subtype === "pie" || element.subtype === "donut";
|
||||
const isApiSource = dataSource.type === "api";
|
||||
@@ -205,14 +238,18 @@ export function ElementConfigSidebar({ element, isOpen, onClose, onApply }: Elem
|
||||
const canApply =
|
||||
isTitleChanged ||
|
||||
isHeaderChanged ||
|
||||
(isSimpleWidget
|
||||
? queryResult && queryResult.rows.length > 0
|
||||
: isMapWidget
|
||||
? queryResult && queryResult.rows.length > 0 && chartConfig.latitudeColumn && chartConfig.longitudeColumn
|
||||
: queryResult &&
|
||||
queryResult.rows.length > 0 &&
|
||||
chartConfig.xAxis &&
|
||||
(isPieChart || isApiSource ? (chartConfig.aggregation === "count" ? true : hasYAxis) : hasYAxis));
|
||||
(isMultiDataSourceWidget
|
||||
? true // 다중 데이터 소스 위젯은 항상 적용 가능
|
||||
: isSimpleWidget
|
||||
? queryResult && queryResult.rows.length > 0
|
||||
: isMapWidget
|
||||
? element.subtype === "map-test"
|
||||
? chartConfig.tileMapUrl || (queryResult && queryResult.rows.length > 0) // 🧪 지도 테스트 위젯: 타일맵 URL 또는 API 데이터
|
||||
: queryResult && queryResult.rows.length > 0 && chartConfig.latitudeColumn && chartConfig.longitudeColumn
|
||||
: queryResult &&
|
||||
queryResult.rows.length > 0 &&
|
||||
chartConfig.xAxis &&
|
||||
(isPieChart || isApiSource ? (chartConfig.aggregation === "count" ? true : hasYAxis) : hasYAxis));
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -269,8 +306,48 @@ export function ElementConfigSidebar({ element, isOpen, onClose, onApply }: Elem
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 다중 데이터 소스 위젯 */}
|
||||
{isMultiDataSourceWidget && (
|
||||
<>
|
||||
<div className="rounded-lg bg-white p-3 shadow-sm">
|
||||
<MultiDataSourceConfig dataSources={dataSources} onChange={setDataSources} />
|
||||
</div>
|
||||
|
||||
{/* 지도 테스트 V2: 타일맵 URL 설정 */}
|
||||
{element.subtype === "map-test-v2" && (
|
||||
<div className="rounded-lg bg-white shadow-sm">
|
||||
<details className="group">
|
||||
<summary className="flex cursor-pointer items-center justify-between p-3 hover:bg-gray-50">
|
||||
<div>
|
||||
<div className="text-xs font-semibold tracking-wide text-gray-500 uppercase">
|
||||
타일맵 설정 (선택사항)
|
||||
</div>
|
||||
<div className="text-muted-foreground mt-0.5 text-[10px]">기본 VWorld 타일맵 사용 중</div>
|
||||
</div>
|
||||
<svg
|
||||
className="h-4 w-4 transition-transform group-open:rotate-180"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</summary>
|
||||
<div className="border-t p-3">
|
||||
<MapTestConfigPanel
|
||||
config={chartConfig}
|
||||
queryResult={undefined}
|
||||
onConfigChange={handleChartConfigChange}
|
||||
/>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 헤더 전용 위젯이 아닐 때만 데이터 소스 표시 */}
|
||||
{!isHeaderOnlyWidget && (
|
||||
{!isHeaderOnlyWidget && !isMultiDataSourceWidget && (
|
||||
<div className="rounded-lg bg-white p-3 shadow-sm">
|
||||
<div className="mb-2 text-[10px] font-semibold tracking-wide text-gray-500 uppercase">데이터 소스</div>
|
||||
|
||||
@@ -303,52 +380,82 @@ export function ElementConfigSidebar({ element, isOpen, onClose, onApply }: Elem
|
||||
/>
|
||||
|
||||
{/* 차트/지도 설정 */}
|
||||
{!isSimpleWidget && queryResult && queryResult.rows.length > 0 && (
|
||||
<div className="mt-2">
|
||||
{isMapWidget ? (
|
||||
<VehicleMapConfigPanel
|
||||
config={chartConfig}
|
||||
queryResult={queryResult}
|
||||
onConfigChange={handleChartConfigChange}
|
||||
/>
|
||||
) : (
|
||||
<ChartConfigPanel
|
||||
config={chartConfig}
|
||||
queryResult={queryResult}
|
||||
onConfigChange={handleChartConfigChange}
|
||||
chartType={element.subtype}
|
||||
dataSourceType={dataSource.type}
|
||||
query={dataSource.query}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{!isSimpleWidget &&
|
||||
(element.subtype === "map-test" || (queryResult && queryResult.rows.length > 0)) && (
|
||||
<div className="mt-2">
|
||||
{isMapWidget ? (
|
||||
element.subtype === "map-test" ? (
|
||||
<MapTestConfigPanel
|
||||
config={chartConfig}
|
||||
queryResult={queryResult || undefined}
|
||||
onConfigChange={handleChartConfigChange}
|
||||
/>
|
||||
) : (
|
||||
queryResult &&
|
||||
queryResult.rows.length > 0 && (
|
||||
<VehicleMapConfigPanel
|
||||
config={chartConfig}
|
||||
queryResult={queryResult}
|
||||
onConfigChange={handleChartConfigChange}
|
||||
/>
|
||||
)
|
||||
)
|
||||
) : (
|
||||
queryResult &&
|
||||
queryResult.rows.length > 0 && (
|
||||
<ChartConfigPanel
|
||||
config={chartConfig}
|
||||
queryResult={queryResult}
|
||||
onConfigChange={handleChartConfigChange}
|
||||
chartType={element.subtype}
|
||||
dataSourceType={dataSource.type}
|
||||
query={dataSource.query}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="api" className="mt-2 space-y-2">
|
||||
<ApiConfig dataSource={dataSource} onChange={handleDataSourceUpdate} onTestResult={handleQueryTest} />
|
||||
|
||||
{/* 차트/지도 설정 */}
|
||||
{!isSimpleWidget && queryResult && queryResult.rows.length > 0 && (
|
||||
<div className="mt-2">
|
||||
{isMapWidget ? (
|
||||
<VehicleMapConfigPanel
|
||||
config={chartConfig}
|
||||
queryResult={queryResult}
|
||||
onConfigChange={handleChartConfigChange}
|
||||
/>
|
||||
) : (
|
||||
<ChartConfigPanel
|
||||
config={chartConfig}
|
||||
queryResult={queryResult}
|
||||
onConfigChange={handleChartConfigChange}
|
||||
chartType={element.subtype}
|
||||
dataSourceType={dataSource.type}
|
||||
query={dataSource.query}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{!isSimpleWidget &&
|
||||
(element.subtype === "map-test" || (queryResult && queryResult.rows.length > 0)) && (
|
||||
<div className="mt-2">
|
||||
{isMapWidget ? (
|
||||
element.subtype === "map-test" ? (
|
||||
<MapTestConfigPanel
|
||||
config={chartConfig}
|
||||
queryResult={queryResult || undefined}
|
||||
onConfigChange={handleChartConfigChange}
|
||||
/>
|
||||
) : (
|
||||
queryResult &&
|
||||
queryResult.rows.length > 0 && (
|
||||
<VehicleMapConfigPanel
|
||||
config={chartConfig}
|
||||
queryResult={queryResult}
|
||||
onConfigChange={handleChartConfigChange}
|
||||
/>
|
||||
)
|
||||
)
|
||||
) : (
|
||||
queryResult &&
|
||||
queryResult.rows.length > 0 && (
|
||||
<ChartConfigPanel
|
||||
config={chartConfig}
|
||||
queryResult={queryResult}
|
||||
onConfigChange={handleChartConfigChange}
|
||||
chartType={element.subtype}
|
||||
dataSourceType={dataSource.type}
|
||||
query={dataSource.query}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user