fix: 테스트 위젯 최종 수정 및 충돌 해결
This commit is contained in:
parent
8edd5e4ca6
commit
398c47618b
|
|
@ -185,7 +185,7 @@ export function DashboardTopMenu({
|
|||
<SelectGroup>
|
||||
<SelectLabel>데이터 위젯</SelectLabel>
|
||||
<SelectItem value="map-summary-v2">지도</SelectItem>
|
||||
{/* <SelectItem value="chart">차트</SelectItem> */} {/* 주석 처리: 2025-10-29, 시기상조 */}
|
||||
<SelectItem value="chart">테스트용 차트 위젯</SelectItem>
|
||||
<SelectItem value="list-v2">리스트</SelectItem>
|
||||
<SelectItem value="custom-metric-v2">통계 카드</SelectItem>
|
||||
<SelectItem value="risk-alert-v2">리스크/알림</SelectItem>
|
||||
|
|
|
|||
|
|
@ -157,11 +157,14 @@ export function MultiChartConfigPanel({
|
|||
<SelectValue placeholder="차트 타입 선택" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="line">라인 차트</SelectItem>
|
||||
<SelectItem value="bar">바 차트</SelectItem>
|
||||
<SelectItem value="area">영역 차트</SelectItem>
|
||||
<SelectItem value="pie">파이 차트</SelectItem>
|
||||
<SelectItem value="donut">도넛 차트</SelectItem>
|
||||
<SelectItem value="line">📈 라인 차트</SelectItem>
|
||||
<SelectItem value="bar">📊 바 차트</SelectItem>
|
||||
<SelectItem value="horizontal-bar">📊 수평 바 차트</SelectItem>
|
||||
<SelectItem value="stacked-bar">📊 누적 바 차트</SelectItem>
|
||||
<SelectItem value="area">📉 영역 차트</SelectItem>
|
||||
<SelectItem value="pie">🥧 파이 차트</SelectItem>
|
||||
<SelectItem value="donut">🍩 도넛 차트</SelectItem>
|
||||
<SelectItem value="combo">🎨 콤보 차트</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ export default function ChartTestWidget({ element }: ChartTestWidgetProps) {
|
|||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [containerSize, setContainerSize] = useState({ width: 600, height: 400 });
|
||||
|
||||
console.log("🧪 ChartTestWidget 렌더링 (D3 기반)!", element);
|
||||
// console.log("🧪 ChartTestWidget 렌더링 (D3 기반)!", element);
|
||||
|
||||
const dataSources = useMemo(() => {
|
||||
return element?.dataSources || element?.chartConfig?.dataSources;
|
||||
|
|
@ -48,11 +48,11 @@ export default function ChartTestWidget({ element }: ChartTestWidgetProps) {
|
|||
const dataSources = element?.dataSources || element?.chartConfig?.dataSources;
|
||||
|
||||
if (!dataSources || dataSources.length === 0) {
|
||||
console.log("⚠️ 데이터 소스가 없습니다.");
|
||||
// console.log("⚠️ 데이터 소스가 없습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`🔄 ${dataSources.length}개의 데이터 소스 로딩 시작...`);
|
||||
// console.log(`🔄 ${dataSources.length}개의 데이터 소스 로딩 시작...`);
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ export default function ChartTestWidget({ element }: ChartTestWidgetProps) {
|
|||
const results = await Promise.allSettled(
|
||||
dataSources.map(async (source) => {
|
||||
try {
|
||||
console.log(`📡 데이터 소스 "${source.name || source.id}" 로딩 중...`);
|
||||
// console.log(`📡 데이터 소스 "${source.name || source.id}" 로딩 중...`);
|
||||
|
||||
if (source.type === "api") {
|
||||
return await loadRestApiData(source);
|
||||
|
|
@ -87,7 +87,7 @@ export default function ChartTestWidget({ element }: ChartTestWidgetProps) {
|
|||
}
|
||||
});
|
||||
|
||||
console.log(`✅ 총 ${allData.length}개의 데이터 로딩 완료`);
|
||||
// console.log(`✅ 총 ${allData.length}개의 데이터 로딩 완료`);
|
||||
setData(allData);
|
||||
setLastRefreshTime(new Date());
|
||||
} catch (err: any) {
|
||||
|
|
@ -100,7 +100,7 @@ export default function ChartTestWidget({ element }: ChartTestWidgetProps) {
|
|||
|
||||
// 수동 새로고침
|
||||
const handleManualRefresh = useCallback(() => {
|
||||
console.log("🔄 수동 새로고침 버튼 클릭");
|
||||
// console.log("🔄 수동 새로고침 버튼 클릭");
|
||||
loadMultipleDataSources();
|
||||
}, [loadMultipleDataSources]);
|
||||
|
||||
|
|
@ -212,15 +212,15 @@ export default function ChartTestWidget({ element }: ChartTestWidgetProps) {
|
|||
if (intervals.length === 0) return;
|
||||
|
||||
const minInterval = Math.min(...intervals);
|
||||
console.log(`⏱️ 자동 새로고침 설정: ${minInterval}초마다`);
|
||||
// console.log(`⏱️ 자동 새로고침 설정: ${minInterval}초마다`);
|
||||
|
||||
const intervalId = setInterval(() => {
|
||||
console.log("🔄 자동 새로고침 실행");
|
||||
// console.log("🔄 자동 새로고침 실행");
|
||||
loadMultipleDataSources();
|
||||
}, minInterval * 1000);
|
||||
|
||||
return () => {
|
||||
console.log("⏹️ 자동 새로고침 정리");
|
||||
// console.log("⏹️ 자동 새로고침 정리");
|
||||
clearInterval(intervalId);
|
||||
};
|
||||
}, [dataSources, loadMultipleDataSources]);
|
||||
|
|
@ -241,14 +241,20 @@ export default function ChartTestWidget({ element }: ChartTestWidgetProps) {
|
|||
|
||||
// 병합 모드: 여러 데이터 소스를 하나로 합침
|
||||
if (mergeMode && dataSourceConfigs.length > 1) {
|
||||
// console.log("🔀 병합 모드 활성화");
|
||||
|
||||
// 모든 데이터 소스의 X축 필드 수집 (첫 번째 데이터 소스의 X축 사용)
|
||||
const baseConfig = dataSourceConfigs[0];
|
||||
const xAxisField = baseConfig.xAxis;
|
||||
const yAxisField = baseConfig.yAxis[0];
|
||||
|
||||
// console.log("📊 X축 필드:", xAxisField);
|
||||
|
||||
// X축 값 수집
|
||||
dataSourceConfigs.forEach((dsConfig) => {
|
||||
// X축 값 수집 (모든 데이터 소스에서)
|
||||
dataSourceConfigs.forEach((dsConfig, idx) => {
|
||||
const sourceName = dataSources?.find((ds) => ds.id === dsConfig.dataSourceId)?.name;
|
||||
const sourceData = data.filter((item) => item._source === sourceName);
|
||||
// console.log(` 소스 ${idx + 1} (${sourceName}): ${sourceData.length}개 행`);
|
||||
|
||||
sourceData.forEach((item) => {
|
||||
if (item[xAxisField] !== undefined) {
|
||||
labels.add(String(item[xAxisField]));
|
||||
|
|
@ -256,26 +262,36 @@ export default function ChartTestWidget({ element }: ChartTestWidgetProps) {
|
|||
});
|
||||
});
|
||||
|
||||
// 데이터 병합
|
||||
const mergedData: number[] = [];
|
||||
labels.forEach((label) => {
|
||||
let totalValue = 0;
|
||||
dataSourceConfigs.forEach((dsConfig) => {
|
||||
const sourceName = dataSources?.find((ds) => ds.id === dsConfig.dataSourceId)?.name;
|
||||
const sourceData = data.filter((item) => item._source === sourceName);
|
||||
const matchingItem = sourceData.find((item) => String(item[xAxisField]) === label);
|
||||
if (matchingItem && yAxisField) {
|
||||
totalValue += parseFloat(matchingItem[yAxisField]) || 0;
|
||||
}
|
||||
// console.log("📍 수집된 X축 라벨:", Array.from(labels));
|
||||
|
||||
// 각 데이터 소스별로 데이터셋 생성 (병합하지 않고 각각 표시)
|
||||
dataSourceConfigs.forEach((dsConfig, idx) => {
|
||||
const sourceName = dataSources?.find((ds) => ds.id === dsConfig.dataSourceId)?.name || `소스 ${idx + 1}`;
|
||||
const sourceData = data.filter((item) => item._source === sourceName);
|
||||
|
||||
// 각 Y축 필드마다 데이터셋 생성
|
||||
(dsConfig.yAxis || []).forEach((yAxisField, yIdx) => {
|
||||
const datasetData: number[] = [];
|
||||
|
||||
labels.forEach((label) => {
|
||||
const matchingItem = sourceData.find((item) => String(item[xAxisField]) === label);
|
||||
const value = matchingItem && yAxisField ? parseFloat(matchingItem[yAxisField]) || 0 : 0;
|
||||
datasetData.push(value);
|
||||
});
|
||||
|
||||
// console.log(` 📈 ${sourceName} - ${yAxisField}: [${datasetData.join(", ")}]`);
|
||||
|
||||
datasets.push({
|
||||
label: `${sourceName} - ${yAxisField}`,
|
||||
data: datasetData,
|
||||
backgroundColor: COLORS[(idx * 2 + yIdx) % COLORS.length],
|
||||
borderColor: COLORS[(idx * 2 + yIdx) % COLORS.length],
|
||||
type: dsConfig.chartType || chartType, // 데이터 소스별 차트 타입
|
||||
});
|
||||
});
|
||||
mergedData.push(totalValue);
|
||||
});
|
||||
|
||||
datasets.push({
|
||||
label: yAxisField,
|
||||
data: mergedData,
|
||||
color: COLORS[0],
|
||||
});
|
||||
// console.log("✅ 병합 모드 데이터셋 생성 완료:", datasets.length, "개");
|
||||
} else {
|
||||
// 일반 모드: 각 데이터 소스를 별도로 표시
|
||||
dataSourceConfigs.forEach((dsConfig, index) => {
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ export default function CustomMetricTestWidget({ element }: CustomMetricTestWidg
|
|||
const [selectedMetric, setSelectedMetric] = useState<any | null>(null);
|
||||
const [isDetailOpen, setIsDetailOpen] = useState(false);
|
||||
|
||||
console.log("🧪 CustomMetricTestWidget 렌더링!", element);
|
||||
// console.log("🧪 CustomMetricTestWidget 렌더링!", element);
|
||||
|
||||
const dataSources = useMemo(() => {
|
||||
return element?.dataSources || element?.chartConfig?.dataSources;
|
||||
|
|
@ -101,10 +101,10 @@ export default function CustomMetricTestWidget({ element }: CustomMetricTestWidg
|
|||
const { dashboardApi } = await import("@/lib/api/dashboard");
|
||||
const result = await dashboardApi.executeQuery(groupByDS.query);
|
||||
|
||||
if (result.success && result.data?.rows) {
|
||||
const rows = result.data.rows;
|
||||
if (result && result.rows) {
|
||||
const rows = result.rows;
|
||||
if (rows.length > 0) {
|
||||
const columns = result.data.columns || Object.keys(rows[0]);
|
||||
const columns = result.columns || Object.keys(rows[0]);
|
||||
const labelColumn = columns[0];
|
||||
const valueColumn = columns[1];
|
||||
|
||||
|
|
@ -121,12 +121,17 @@ export default function CustomMetricTestWidget({ element }: CustomMetricTestWidg
|
|||
else if (dataSourceType === "api") {
|
||||
if (!groupByDS.endpoint) return;
|
||||
|
||||
const { dashboardApi } = await import("@/lib/api/dashboard");
|
||||
const result = await dashboardApi.fetchExternalApi({
|
||||
method: "GET",
|
||||
url: groupByDS.endpoint,
|
||||
headers: (groupByDS as any).headers || {},
|
||||
// REST API 호출 (백엔드 프록시 사용)
|
||||
const response = await fetch(getApiUrl("/api/dashboards/fetch-external-api"), {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
url: groupByDS.endpoint,
|
||||
method: "GET",
|
||||
headers: (groupByDS as any).headers || {},
|
||||
}),
|
||||
});
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success && result.data) {
|
||||
let rows: any[] = [];
|
||||
|
|
@ -163,11 +168,11 @@ export default function CustomMetricTestWidget({ element }: CustomMetricTestWidg
|
|||
const dataSources = element?.dataSources || element?.chartConfig?.dataSources;
|
||||
|
||||
if (!dataSources || dataSources.length === 0) {
|
||||
console.log("⚠️ 데이터 소스가 없습니다.");
|
||||
// console.log("⚠️ 데이터 소스가 없습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`🔄 ${dataSources.length}개의 데이터 소스 로딩 시작...`);
|
||||
// console.log(`🔄 ${dataSources.length}개의 데이터 소스 로딩 시작...`);
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
|
|
@ -176,7 +181,7 @@ export default function CustomMetricTestWidget({ element }: CustomMetricTestWidg
|
|||
const results = await Promise.allSettled(
|
||||
dataSources.map(async (source, sourceIndex) => {
|
||||
try {
|
||||
console.log(`📡 데이터 소스 ${sourceIndex + 1} "${source.name || source.id}" 로딩 중...`);
|
||||
// console.log(`📡 데이터 소스 ${sourceIndex + 1} "${source.name || source.id}" 로딩 중...`);
|
||||
|
||||
let rows: any[] = [];
|
||||
if (source.type === "api") {
|
||||
|
|
@ -185,7 +190,7 @@ export default function CustomMetricTestWidget({ element }: CustomMetricTestWidg
|
|||
rows = await loadDatabaseData(source);
|
||||
}
|
||||
|
||||
console.log(`✅ 데이터 소스 ${sourceIndex + 1}: ${rows.length}개 행`);
|
||||
// console.log(`✅ 데이터 소스 ${sourceIndex + 1}: ${rows.length}개 행`);
|
||||
|
||||
return {
|
||||
sourceName: source.name || `데이터 소스 ${sourceIndex + 1}`,
|
||||
|
|
@ -203,7 +208,7 @@ export default function CustomMetricTestWidget({ element }: CustomMetricTestWidg
|
|||
}),
|
||||
);
|
||||
|
||||
console.log(`✅ 총 ${results.length}개의 데이터 소스 로딩 완료`);
|
||||
// console.log(`✅ 총 ${results.length}개의 데이터 소스 로딩 완료`);
|
||||
|
||||
// 각 데이터 소스별로 메트릭 생성
|
||||
const allMetrics: any[] = [];
|
||||
|
|
@ -235,11 +240,11 @@ export default function CustomMetricTestWidget({ element }: CustomMetricTestWidg
|
|||
return typeof value === "string" || !numericColumns.includes(col);
|
||||
});
|
||||
|
||||
console.log(`📊 [${sourceName}] 컬럼 분석:`, {
|
||||
전체: columns,
|
||||
숫자: numericColumns,
|
||||
문자열: stringColumns,
|
||||
});
|
||||
// console.log(`📊 [${sourceName}] 컬럼 분석:`, {
|
||||
// 전체: columns,
|
||||
// 숫자: numericColumns,
|
||||
// 문자열: stringColumns,
|
||||
// });
|
||||
|
||||
// 🆕 자동 집계 로직: 집계 컬럼 이름으로 판단 (count, 개수, sum, avg 등)
|
||||
const isAggregated = numericColumns.some((col) =>
|
||||
|
|
@ -248,7 +253,7 @@ export default function CustomMetricTestWidget({ element }: CustomMetricTestWidg
|
|||
|
||||
if (isAggregated && numericColumns.length > 0) {
|
||||
// 집계 컬럼이 있으면 이미 집계된 데이터로 판단 (GROUP BY 결과)
|
||||
console.log(`✅ [${sourceName}] 집계된 데이터로 판단 (집계 컬럼 발견: ${numericColumns.join(", ")})`);
|
||||
// console.log(`✅ [${sourceName}] 집계된 데이터로 판단 (집계 컬럼 발견: ${numericColumns.join(", ")})`);
|
||||
|
||||
rows.forEach((row, index) => {
|
||||
// 라벨: 첫 번째 문자열 컬럼
|
||||
|
|
@ -259,7 +264,7 @@ export default function CustomMetricTestWidget({ element }: CustomMetricTestWidg
|
|||
const valueField = numericColumns[0];
|
||||
const value = Number(row[valueField]) || 0;
|
||||
|
||||
console.log(` [${sourceName}] 메트릭: ${label} = ${value}`);
|
||||
// console.log(` [${sourceName}] 메트릭: ${label} = ${value}`);
|
||||
|
||||
allMetrics.push({
|
||||
label: label,
|
||||
|
|
@ -273,11 +278,11 @@ export default function CustomMetricTestWidget({ element }: CustomMetricTestWidg
|
|||
});
|
||||
} else {
|
||||
// 숫자 컬럼이 없으면 자동 집계 (마지막 컬럼 기준)
|
||||
console.log(`✅ [${sourceName}] 자동 집계 모드 (숫자 컬럼 없음)`);
|
||||
// console.log(`✅ [${sourceName}] 자동 집계 모드 (숫자 컬럼 없음)`);
|
||||
|
||||
// 마지막 컬럼을 집계 기준으로 사용
|
||||
const aggregateField = columns[columns.length - 1];
|
||||
console.log(` [${sourceName}] 집계 기준 컬럼: ${aggregateField}`);
|
||||
// console.log(` [${sourceName}] 집계 기준 컬럼: ${aggregateField}`);
|
||||
|
||||
// 해당 컬럼의 값별로 카운트
|
||||
const countMap = new Map<string, number>();
|
||||
|
|
@ -288,7 +293,7 @@ export default function CustomMetricTestWidget({ element }: CustomMetricTestWidg
|
|||
|
||||
// 카운트 결과를 메트릭으로 변환
|
||||
countMap.forEach((count, label) => {
|
||||
console.log(` [${sourceName}] 자동 집계: ${label} = ${count}개`);
|
||||
// console.log(` [${sourceName}] 자동 집계: ${label} = ${count}개`);
|
||||
|
||||
allMetrics.push({
|
||||
label: label,
|
||||
|
|
@ -314,20 +319,20 @@ export default function CustomMetricTestWidget({ element }: CustomMetricTestWidg
|
|||
}
|
||||
|
||||
// 🆕 숫자 컬럼이 없을 때의 기존 로직은 주석 처리
|
||||
if (false) {
|
||||
/* if (false && result.status === "fulfilled") {
|
||||
// 숫자 컬럼이 없으면 각 컬럼별 고유값 개수 표시
|
||||
console.log(`📊 [${sourceName}] 문자열 데이터, 각 컬럼별 고유값 개수 표시`);
|
||||
// console.log(`📊 [${sourceName}] 문자열 데이터, 각 컬럼별 고유값 개수 표시`);
|
||||
|
||||
// 데이터 소스에서 선택된 컬럼 가져오기
|
||||
const dataSourceConfig = (element?.dataSources || element?.chartConfig?.dataSources)?.find(
|
||||
(ds) => ds.name === sourceName || ds.id === result.value.sourceIndex.toString(),
|
||||
(ds) => ds.name === sourceName || ds.id === result.value?.sourceIndex.toString(),
|
||||
);
|
||||
const selectedColumns = dataSourceConfig?.selectedColumns || [];
|
||||
|
||||
// 선택된 컬럼이 있으면 해당 컬럼만, 없으면 전체 컬럼 표시
|
||||
const columnsToShow = selectedColumns.length > 0 ? selectedColumns : columns;
|
||||
|
||||
console.log(` [${sourceName}] 표시할 컬럼:`, columnsToShow);
|
||||
// console.log(` [${sourceName}] 표시할 컬럼:`, columnsToShow);
|
||||
|
||||
columnsToShow.forEach((col) => {
|
||||
// 해당 컬럼이 실제로 존재하는지 확인
|
||||
|
|
@ -340,7 +345,7 @@ export default function CustomMetricTestWidget({ element }: CustomMetricTestWidg
|
|||
const uniqueValues = new Set(rows.map((row) => row[col]));
|
||||
const uniqueCount = uniqueValues.size;
|
||||
|
||||
console.log(` [${sourceName}] ${col}: ${uniqueCount}개 고유값`);
|
||||
// console.log(` [${sourceName}] ${col}: ${uniqueCount}개 고유값`);
|
||||
|
||||
allMetrics.push({
|
||||
label: `${sourceName} - ${col} (고유값)`,
|
||||
|
|
@ -363,24 +368,24 @@ export default function CustomMetricTestWidget({ element }: CustomMetricTestWidg
|
|||
sourceName: sourceName,
|
||||
rawData: rows, // 원본 데이터 저장
|
||||
});
|
||||
}
|
||||
} */
|
||||
} else {
|
||||
// 행이 많으면 각 컬럼별 고유값 개수 + 총 개수 표시
|
||||
console.log(`📊 [${sourceName}] 일반 데이터 (행 많음), 컬럼별 통계 표시`);
|
||||
// console.log(`📊 [${sourceName}] 일반 데이터 (행 많음), 컬럼별 통계 표시`);
|
||||
|
||||
const firstRow = rows[0];
|
||||
const columns = Object.keys(firstRow);
|
||||
|
||||
// 데이터 소스에서 선택된 컬럼 가져오기
|
||||
const dataSourceConfig = (element?.dataSources || element?.chartConfig?.dataSources)?.find(
|
||||
(ds) => ds.name === sourceName || ds.id === result.value.sourceIndex.toString(),
|
||||
(ds) => ds.name === sourceName || (result.status === "fulfilled" && ds.id === result.value?.sourceIndex.toString()),
|
||||
);
|
||||
const selectedColumns = dataSourceConfig?.selectedColumns || [];
|
||||
|
||||
// 선택된 컬럼이 있으면 해당 컬럼만, 없으면 전체 컬럼 표시
|
||||
const columnsToShow = selectedColumns.length > 0 ? selectedColumns : columns;
|
||||
|
||||
console.log(` [${sourceName}] 표시할 컬럼:`, columnsToShow);
|
||||
// console.log(` [${sourceName}] 표시할 컬럼:`, columnsToShow);
|
||||
|
||||
// 각 컬럼별 고유값 개수
|
||||
columnsToShow.forEach((col) => {
|
||||
|
|
@ -393,7 +398,7 @@ export default function CustomMetricTestWidget({ element }: CustomMetricTestWidg
|
|||
const uniqueValues = new Set(rows.map((row) => row[col]));
|
||||
const uniqueCount = uniqueValues.size;
|
||||
|
||||
console.log(` [${sourceName}] ${col}: ${uniqueCount}개 고유값`);
|
||||
// console.log(` [${sourceName}] ${col}: ${uniqueCount}개 고유값`);
|
||||
|
||||
allMetrics.push({
|
||||
label: `${sourceName} - ${col} (고유값)`,
|
||||
|
|
@ -419,7 +424,7 @@ export default function CustomMetricTestWidget({ element }: CustomMetricTestWidg
|
|||
}
|
||||
});
|
||||
|
||||
console.log(`✅ 총 ${allMetrics.length}개의 메트릭 생성 완료`);
|
||||
// console.log(`✅ 총 ${allMetrics.length}개의 메트릭 생성 완료`);
|
||||
setMetrics(allMetrics);
|
||||
setLastRefreshTime(new Date());
|
||||
} catch (err) {
|
||||
|
|
@ -460,13 +465,13 @@ export default function CustomMetricTestWidget({ element }: CustomMetricTestWidg
|
|||
|
||||
// 수동 새로고침 핸들러
|
||||
const handleManualRefresh = useCallback(() => {
|
||||
console.log("🔄 수동 새로고침 버튼 클릭");
|
||||
// console.log("🔄 수동 새로고침 버튼 클릭");
|
||||
loadAllData();
|
||||
}, [loadAllData]);
|
||||
|
||||
// XML 데이터 파싱
|
||||
const parseXmlData = (xmlText: string): any[] => {
|
||||
console.log("🔍 XML 파싱 시작");
|
||||
// console.log("🔍 XML 파싱 시작");
|
||||
try {
|
||||
const parser = new DOMParser();
|
||||
const xmlDoc = parser.parseFromString(xmlText, "text/xml");
|
||||
|
|
@ -486,7 +491,7 @@ export default function CustomMetricTestWidget({ element }: CustomMetricTestWidg
|
|||
result.push(obj);
|
||||
}
|
||||
|
||||
console.log(`✅ XML 파싱 완료: ${result.length}개 레코드`);
|
||||
// console.log(`✅ XML 파싱 완료: ${result.length}개 레코드`);
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error("❌ XML 파싱 실패:", error);
|
||||
|
|
@ -496,16 +501,16 @@ export default function CustomMetricTestWidget({ element }: CustomMetricTestWidg
|
|||
|
||||
// 텍스트/CSV 데이터 파싱
|
||||
const parseTextData = (text: string): any[] => {
|
||||
console.log("🔍 텍스트 파싱 시작 (처음 500자):", text.substring(0, 500));
|
||||
// console.log("🔍 텍스트 파싱 시작 (처음 500자):", text.substring(0, 500));
|
||||
|
||||
// XML 감지
|
||||
if (text.trim().startsWith("<?xml") || text.trim().startsWith("<result>")) {
|
||||
console.log("📄 XML 형식 감지");
|
||||
// console.log("📄 XML 형식 감지");
|
||||
return parseXmlData(text);
|
||||
}
|
||||
|
||||
// CSV 파싱
|
||||
console.log("📄 CSV 형식으로 파싱 시도");
|
||||
// console.log("📄 CSV 형식으로 파싱 시도");
|
||||
const lines = text.trim().split("\n");
|
||||
if (lines.length === 0) return [];
|
||||
|
||||
|
|
@ -523,7 +528,7 @@ export default function CustomMetricTestWidget({ element }: CustomMetricTestWidg
|
|||
result.push(obj);
|
||||
}
|
||||
|
||||
console.log(`✅ CSV 파싱 완료: ${result.length}개 행`);
|
||||
// console.log(`✅ CSV 파싱 완료: ${result.length}개 행`);
|
||||
return result;
|
||||
};
|
||||
|
||||
|
|
@ -552,7 +557,7 @@ export default function CustomMetricTestWidget({ element }: CustomMetricTestWidg
|
|||
}
|
||||
}
|
||||
|
||||
console.log("🌐 API 호출:", source.endpoint, "파라미터:", Object.fromEntries(params));
|
||||
// console.log("🌐 API 호출:", source.endpoint, "파라미터:", Object.fromEntries(params));
|
||||
|
||||
const response = await fetch(getApiUrl("/api/dashboards/fetch-external-api"), {
|
||||
method: "POST",
|
||||
|
|
@ -578,7 +583,7 @@ export default function CustomMetricTestWidget({ element }: CustomMetricTestWidg
|
|||
}
|
||||
|
||||
const result = await response.json();
|
||||
console.log("✅ API 응답:", result);
|
||||
// console.log("✅ API 응답:", result);
|
||||
|
||||
if (!result.success) {
|
||||
console.error("❌ API 실패:", result);
|
||||
|
|
@ -589,10 +594,10 @@ export default function CustomMetricTestWidget({ element }: CustomMetricTestWidg
|
|||
|
||||
// 텍스트/XML 데이터 처리
|
||||
if (typeof processedData === "string") {
|
||||
console.log("📄 텍스트 형식 데이터 감지");
|
||||
// console.log("📄 텍스트 형식 데이터 감지");
|
||||
processedData = parseTextData(processedData);
|
||||
} else if (processedData && typeof processedData === "object" && processedData.text) {
|
||||
console.log("📄 래핑된 텍스트 데이터 감지");
|
||||
// console.log("📄 래핑된 텍스트 데이터 감지");
|
||||
processedData = parseTextData(processedData.text);
|
||||
}
|
||||
|
||||
|
|
@ -608,12 +613,12 @@ export default function CustomMetricTestWidget({ element }: CustomMetricTestWidg
|
|||
}
|
||||
} else if (!Array.isArray(processedData) && typeof processedData === "object") {
|
||||
// JSON Path 없으면 자동으로 배열 찾기
|
||||
console.log("🔍 JSON Path 없음, 자동으로 배열 찾기 시도");
|
||||
// console.log("🔍 JSON Path 없음, 자동으로 배열 찾기 시도");
|
||||
const arrayKeys = ["data", "items", "result", "records", "rows", "list"];
|
||||
|
||||
for (const key of arrayKeys) {
|
||||
if (Array.isArray(processedData[key])) {
|
||||
console.log(`✅ 배열 발견: ${key}`);
|
||||
// console.log(`✅ 배열 발견: ${key}`);
|
||||
processedData = processedData[key];
|
||||
break;
|
||||
}
|
||||
|
|
@ -681,15 +686,15 @@ export default function CustomMetricTestWidget({ element }: CustomMetricTestWidg
|
|||
if (intervals.length === 0) return;
|
||||
|
||||
const minInterval = Math.min(...intervals);
|
||||
console.log(`⏱️ 자동 새로고침 설정: ${minInterval}초마다`);
|
||||
// console.log(`⏱️ 자동 새로고침 설정: ${minInterval}초마다`);
|
||||
|
||||
const intervalId = setInterval(() => {
|
||||
console.log("🔄 자동 새로고침 실행");
|
||||
// console.log("🔄 자동 새로고침 실행");
|
||||
loadAllData();
|
||||
}, minInterval * 1000);
|
||||
|
||||
return () => {
|
||||
console.log("⏹️ 자동 새로고침 정리");
|
||||
// console.log("⏹️ 자동 새로고침 정리");
|
||||
clearInterval(intervalId);
|
||||
};
|
||||
}, [dataSources, loadAllData]);
|
||||
|
|
|
|||
|
|
@ -34,13 +34,13 @@ export function ListTestWidget({ element }: ListTestWidgetProps) {
|
|||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [lastRefreshTime, setLastRefreshTime] = useState<Date | null>(null);
|
||||
|
||||
// console.log("🧪 ListTestWidget 렌더링!", element);
|
||||
// // console.log("🧪 ListTestWidget 렌더링!", element);
|
||||
|
||||
const dataSources = useMemo(() => {
|
||||
return element?.dataSources || element?.chartConfig?.dataSources;
|
||||
}, [element?.dataSources, element?.chartConfig?.dataSources]);
|
||||
|
||||
// console.log("📊 dataSources 확인:", {
|
||||
// // console.log("📊 dataSources 확인:", {
|
||||
// hasDataSources: !!dataSources,
|
||||
// dataSourcesLength: dataSources?.length || 0,
|
||||
// dataSources: dataSources,
|
||||
|
|
@ -61,11 +61,11 @@ export function ListTestWidget({ element }: ListTestWidgetProps) {
|
|||
// 다중 데이터 소스 로딩
|
||||
const loadMultipleDataSources = useCallback(async () => {
|
||||
if (!dataSources || dataSources.length === 0) {
|
||||
console.log("⚠️ 데이터 소스가 없습니다.");
|
||||
// console.log("⚠️ 데이터 소스가 없습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`🔄 ${dataSources.length}개의 데이터 소스 로딩 시작...`);
|
||||
// console.log(`🔄 ${dataSources.length}개의 데이터 소스 로딩 시작...`);
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
||||
|
|
@ -74,7 +74,7 @@ export function ListTestWidget({ element }: ListTestWidgetProps) {
|
|||
const results = await Promise.allSettled(
|
||||
dataSources.map(async (source) => {
|
||||
try {
|
||||
console.log(`📡 데이터 소스 "${source.name || source.id}" 로딩 중...`);
|
||||
// console.log(`📡 데이터 소스 "${source.name || source.id}" 로딩 중...`);
|
||||
|
||||
if (source.type === "api") {
|
||||
return await loadRestApiData(source);
|
||||
|
|
@ -127,7 +127,7 @@ export function ListTestWidget({ element }: ListTestWidgetProps) {
|
|||
});
|
||||
setLastRefreshTime(new Date());
|
||||
|
||||
console.log(`✅ 총 ${allRows.length}개의 행 로딩 완료`);
|
||||
// console.log(`✅ 총 ${allRows.length}개의 행 로딩 완료`);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : "데이터 로딩 실패");
|
||||
} finally {
|
||||
|
|
@ -137,7 +137,7 @@ export function ListTestWidget({ element }: ListTestWidgetProps) {
|
|||
|
||||
// 수동 새로고침 핸들러
|
||||
const handleManualRefresh = useCallback(() => {
|
||||
console.log("🔄 수동 새로고침 버튼 클릭");
|
||||
// console.log("🔄 수동 새로고침 버튼 클릭");
|
||||
loadMultipleDataSources();
|
||||
}, [loadMultipleDataSources]);
|
||||
|
||||
|
|
@ -161,6 +161,7 @@ export function ListTestWidget({ element }: ListTestWidgetProps) {
|
|||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
credentials: "include",
|
||||
body: JSON.stringify({
|
||||
url: source.endpoint,
|
||||
method: "GET",
|
||||
|
|
@ -180,7 +181,7 @@ export function ListTestWidget({ element }: ListTestWidgetProps) {
|
|||
}
|
||||
|
||||
const result = await response.json();
|
||||
console.log("✅ API 응답:", result);
|
||||
// console.log("✅ API 응답:", result);
|
||||
|
||||
if (!result.success) {
|
||||
console.error("❌ API 실패:", result);
|
||||
|
|
@ -247,7 +248,7 @@ export function ListTestWidget({ element }: ListTestWidgetProps) {
|
|||
const { dashboardApi } = await import("@/lib/api/dashboard");
|
||||
const result = await dashboardApi.executeQuery(source.query);
|
||||
|
||||
// console.log("💾 내부 DB 쿼리 결과:", {
|
||||
// // console.log("💾 내부 DB 쿼리 결과:", {
|
||||
// hasRows: !!result.rows,
|
||||
// rowCount: result.rows?.length || 0,
|
||||
// hasColumns: !!result.columns,
|
||||
|
|
@ -260,7 +261,7 @@ export function ListTestWidget({ element }: ListTestWidgetProps) {
|
|||
const mappedRows = applyColumnMapping(result.rows, source.columnMapping);
|
||||
const columns = mappedRows.length > 0 ? Object.keys(mappedRows[0]) : result.columns;
|
||||
|
||||
// console.log("✅ 매핑 후:", {
|
||||
// // console.log("✅ 매핑 후:", {
|
||||
// columns,
|
||||
// rowCount: mappedRows.length,
|
||||
// firstMappedRow: mappedRows[0],
|
||||
|
|
@ -291,15 +292,15 @@ export function ListTestWidget({ element }: ListTestWidgetProps) {
|
|||
if (intervals.length === 0) return;
|
||||
|
||||
const minInterval = Math.min(...intervals);
|
||||
console.log(`⏱️ 자동 새로고침 설정: ${minInterval}초마다`);
|
||||
// console.log(`⏱️ 자동 새로고침 설정: ${minInterval}초마다`);
|
||||
|
||||
const intervalId = setInterval(() => {
|
||||
console.log("🔄 자동 새로고침 실행");
|
||||
// console.log("🔄 자동 새로고침 실행");
|
||||
loadMultipleDataSources();
|
||||
}, minInterval * 1000);
|
||||
|
||||
return () => {
|
||||
console.log("⏹️ 자동 새로고침 정리");
|
||||
// console.log("⏹️ 자동 새로고침 정리");
|
||||
clearInterval(intervalId);
|
||||
};
|
||||
}, [dataSources, loadMultipleDataSources]);
|
||||
|
|
|
|||
|
|
@ -67,8 +67,8 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
|
|||
const [geoJsonData, setGeoJsonData] = useState<any>(null);
|
||||
const [lastRefreshTime, setLastRefreshTime] = useState<Date | null>(null);
|
||||
|
||||
console.log("🧪 MapTestWidgetV2 렌더링!", element);
|
||||
console.log("📍 마커:", markers.length, "🔷 폴리곤:", polygons.length);
|
||||
// // console.log("🧪 MapTestWidgetV2 렌더링!", element);
|
||||
// // console.log("📍 마커:", markers.length, "🔷 폴리곤:", polygons.length);
|
||||
|
||||
// dataSources를 useMemo로 추출 (circular reference 방지)
|
||||
const dataSources = useMemo(() => {
|
||||
|
|
@ -80,11 +80,11 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
|
|||
const dataSourcesList = dataSources;
|
||||
|
||||
if (!dataSources || dataSources.length === 0) {
|
||||
console.log("⚠️ 데이터 소스가 없습니다.");
|
||||
// // console.log("⚠️ 데이터 소스가 없습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`🔄 ${dataSources.length}개의 데이터 소스 로딩 시작...`);
|
||||
// // console.log(`🔄 ${dataSources.length}개의 데이터 소스 로딩 시작...`);
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
|
|
@ -93,7 +93,7 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
|
|||
const results = await Promise.allSettled(
|
||||
dataSources.map(async (source) => {
|
||||
try {
|
||||
console.log(`📡 데이터 소스 "${source.name || source.id}" 로딩 중...`);
|
||||
// // console.log(`📡 데이터 소스 "${source.name || source.id}" 로딩 중...`);
|
||||
|
||||
if (source.type === "api") {
|
||||
return await loadRestApiData(source);
|
||||
|
|
@ -114,21 +114,21 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
|
|||
const allPolygons: PolygonData[] = [];
|
||||
|
||||
results.forEach((result, index) => {
|
||||
console.log(`🔍 결과 ${index}:`, result);
|
||||
// // console.log(`🔍 결과 ${index}:`, result);
|
||||
|
||||
if (result.status === "fulfilled" && result.value) {
|
||||
const value = result.value as { markers: MarkerData[]; polygons: PolygonData[] };
|
||||
console.log(`✅ 데이터 소스 ${index} 성공:`, value);
|
||||
// // console.log(`✅ 데이터 소스 ${index} 성공:`, value);
|
||||
|
||||
// 마커 병합
|
||||
if (value.markers && Array.isArray(value.markers)) {
|
||||
console.log(` → 마커 ${value.markers.length}개 추가`);
|
||||
// // console.log(` → 마커 ${value.markers.length}개 추가`);
|
||||
allMarkers.push(...value.markers);
|
||||
}
|
||||
|
||||
// 폴리곤 병합
|
||||
if (value.polygons && Array.isArray(value.polygons)) {
|
||||
console.log(` → 폴리곤 ${value.polygons.length}개 추가`);
|
||||
// // console.log(` → 폴리곤 ${value.polygons.length}개 추가`);
|
||||
allPolygons.push(...value.polygons);
|
||||
}
|
||||
} else if (result.status === "rejected") {
|
||||
|
|
@ -136,9 +136,9 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
|
|||
}
|
||||
});
|
||||
|
||||
console.log(`✅ 총 ${allMarkers.length}개의 마커, ${allPolygons.length}개의 폴리곤 로딩 완료`);
|
||||
console.log("📍 최종 마커 데이터:", allMarkers);
|
||||
console.log("🔷 최종 폴리곤 데이터:", allPolygons);
|
||||
// // console.log(`✅ 총 ${allMarkers.length}개의 마커, ${allPolygons.length}개의 폴리곤 로딩 완료`);
|
||||
// // console.log("📍 최종 마커 데이터:", allMarkers);
|
||||
// // console.log("🔷 최종 폴리곤 데이터:", allPolygons);
|
||||
|
||||
setMarkers(allMarkers);
|
||||
setPolygons(allPolygons);
|
||||
|
|
@ -153,13 +153,13 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
|
|||
|
||||
// 수동 새로고침 핸들러
|
||||
const handleManualRefresh = useCallback(() => {
|
||||
console.log("🔄 수동 새로고침 버튼 클릭");
|
||||
// // console.log("🔄 수동 새로고침 버튼 클릭");
|
||||
loadMultipleDataSources();
|
||||
}, [loadMultipleDataSources]);
|
||||
|
||||
// REST API 데이터 로딩
|
||||
const loadRestApiData = async (source: ChartDataSource): Promise<{ markers: MarkerData[]; polygons: PolygonData[] }> => {
|
||||
console.log(`🌐 REST API 데이터 로딩 시작:`, source.name, `mapDisplayType:`, source.mapDisplayType);
|
||||
// // console.log(`🌐 REST API 데이터 로딩 시작:`, source.name, `mapDisplayType:`, source.mapDisplayType);
|
||||
|
||||
if (!source.endpoint) {
|
||||
throw new Error("API endpoint가 없습니다.");
|
||||
|
|
@ -215,10 +215,10 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
|
|||
|
||||
// 텍스트 형식 데이터 체크 (기상청 API 등)
|
||||
if (data && typeof data === 'object' && data.text && typeof data.text === 'string') {
|
||||
console.log("📄 텍스트 형식 데이터 감지, CSV 파싱 시도");
|
||||
// // console.log("📄 텍스트 형식 데이터 감지, CSV 파싱 시도");
|
||||
const parsedData = parseTextData(data.text);
|
||||
if (parsedData.length > 0) {
|
||||
console.log(`✅ CSV 파싱 성공: ${parsedData.length}개 행`);
|
||||
// // console.log(`✅ CSV 파싱 성공: ${parsedData.length}개 행`);
|
||||
// 컬럼 매핑 적용
|
||||
const mappedData = applyColumnMapping(parsedData, source.columnMapping);
|
||||
return convertToMapData(mappedData, source.name || source.id || "API", source.mapDisplayType, source);
|
||||
|
|
@ -244,7 +244,7 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
|
|||
|
||||
// Database 데이터 로딩
|
||||
const loadDatabaseData = async (source: ChartDataSource): Promise<{ markers: MarkerData[]; polygons: PolygonData[] }> => {
|
||||
console.log(`💾 Database 데이터 로딩 시작:`, source.name, `mapDisplayType:`, source.mapDisplayType);
|
||||
// // console.log(`💾 Database 데이터 로딩 시작:`, source.name, `mapDisplayType:`, source.mapDisplayType);
|
||||
|
||||
if (!source.query) {
|
||||
throw new Error("SQL 쿼리가 없습니다.");
|
||||
|
|
@ -287,7 +287,7 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
|
|||
// XML 데이터 파싱 (UTIC API 등)
|
||||
const parseXmlData = (xmlText: string): any[] => {
|
||||
try {
|
||||
console.log(" 📄 XML 파싱 시작");
|
||||
// // console.log(" 📄 XML 파싱 시작");
|
||||
const parser = new DOMParser();
|
||||
const xmlDoc = parser.parseFromString(xmlText, "text/xml");
|
||||
|
||||
|
|
@ -306,7 +306,7 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
|
|||
results.push(obj);
|
||||
}
|
||||
|
||||
console.log(` ✅ XML 파싱 완료: ${results.length}개 레코드`);
|
||||
// // console.log(` ✅ XML 파싱 완료: ${results.length}개 레코드`);
|
||||
return results;
|
||||
} catch (error) {
|
||||
console.error(" ❌ XML 파싱 실패:", error);
|
||||
|
|
@ -317,11 +317,11 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
|
|||
// 텍스트 데이터 파싱 (CSV, 기상청 형식 등)
|
||||
const parseTextData = (text: string): any[] => {
|
||||
try {
|
||||
console.log(" 🔍 원본 텍스트 (처음 500자):", text.substring(0, 500));
|
||||
// // console.log(" 🔍 원본 텍스트 (처음 500자):", text.substring(0, 500));
|
||||
|
||||
// XML 형식 감지
|
||||
if (text.trim().startsWith("<?xml") || text.trim().startsWith("<result>")) {
|
||||
console.log(" 📄 XML 형식 데이터 감지");
|
||||
// // console.log(" 📄 XML 형식 데이터 감지");
|
||||
return parseXmlData(text);
|
||||
}
|
||||
|
||||
|
|
@ -333,7 +333,7 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
|
|||
!trimmed.startsWith('---');
|
||||
});
|
||||
|
||||
console.log(` 📝 유효한 라인: ${lines.length}개`);
|
||||
// // console.log(` 📝 유효한 라인: ${lines.length}개`);
|
||||
|
||||
if (lines.length === 0) return [];
|
||||
|
||||
|
|
@ -344,7 +344,7 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
|
|||
const line = lines[i];
|
||||
const values = line.split(',').map(v => v.trim().replace(/,=$/g, ''));
|
||||
|
||||
console.log(` 라인 ${i}:`, values);
|
||||
// // console.log(` 라인 ${i}:`, values);
|
||||
|
||||
// 기상특보 형식: 지역코드, 지역명, 하위코드, 하위지역명, 발표시각, 특보종류, 등급, 발표상태, 설명
|
||||
if (values.length >= 4) {
|
||||
|
|
@ -364,11 +364,11 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
|
|||
obj.name = obj.subRegion || obj.region || obj.code;
|
||||
|
||||
result.push(obj);
|
||||
console.log(` ✅ 파싱 성공:`, obj);
|
||||
// console.log(` ✅ 파싱 성공:`, obj);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(" 📊 최종 파싱 결과:", result.length, "개");
|
||||
// // console.log(" 📊 최종 파싱 결과:", result.length, "개");
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error(" ❌ 텍스트 파싱 오류:", error);
|
||||
|
|
@ -383,9 +383,9 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
|
|||
mapDisplayType?: "auto" | "marker" | "polygon",
|
||||
dataSource?: ChartDataSource
|
||||
): { markers: MarkerData[]; polygons: PolygonData[] } => {
|
||||
console.log(`🔄 ${sourceName} 데이터 변환 시작:`, rows.length, "개 행");
|
||||
console.log(` 📌 mapDisplayType:`, mapDisplayType, `(타입: ${typeof mapDisplayType})`);
|
||||
console.log(` 🎨 마커 색상:`, dataSource?.markerColor, `폴리곤 색상:`, dataSource?.polygonColor);
|
||||
// // console.log(`🔄 ${sourceName} 데이터 변환 시작:`, rows.length, "개 행");
|
||||
// // console.log(` 📌 mapDisplayType:`, mapDisplayType, `(타입: ${typeof mapDisplayType})`);
|
||||
// // console.log(` 🎨 마커 색상:`, dataSource?.markerColor, `폴리곤 색상:`, dataSource?.polygonColor);
|
||||
|
||||
if (rows.length === 0) return { markers: [], polygons: [] };
|
||||
|
||||
|
|
@ -393,13 +393,13 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
|
|||
const polygons: PolygonData[] = [];
|
||||
|
||||
rows.forEach((row, index) => {
|
||||
console.log(` 행 ${index}:`, row);
|
||||
// // console.log(` 행 ${index}:`, row);
|
||||
|
||||
// 텍스트 데이터 체크 (기상청 API 등)
|
||||
if (row && typeof row === 'object' && row.text && typeof row.text === 'string') {
|
||||
console.log(" 📄 텍스트 형식 데이터 감지, CSV 파싱 시도");
|
||||
// // console.log(" 📄 텍스트 형식 데이터 감지, CSV 파싱 시도");
|
||||
const parsedData = parseTextData(row.text);
|
||||
console.log(` ✅ CSV 파싱 결과: ${parsedData.length}개 행`);
|
||||
// // console.log(` ✅ CSV 파싱 결과: ${parsedData.length}개 행`);
|
||||
|
||||
// 파싱된 데이터를 재귀적으로 변환 (색상 정보 전달)
|
||||
const result = convertToMapData(parsedData, sourceName, mapDisplayType, dataSource);
|
||||
|
|
@ -410,11 +410,11 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
|
|||
|
||||
// 폴리곤 데이터 체크 (coordinates 필드가 배열인 경우 또는 강제 polygon 모드)
|
||||
if (row.coordinates && Array.isArray(row.coordinates) && row.coordinates.length > 0) {
|
||||
console.log(` → coordinates 발견:`, row.coordinates.length, "개");
|
||||
// // console.log(` → coordinates 발견:`, row.coordinates.length, "개");
|
||||
// coordinates가 [lat, lng] 배열의 배열인지 확인
|
||||
const firstCoord = row.coordinates[0];
|
||||
if (Array.isArray(firstCoord) && firstCoord.length === 2) {
|
||||
console.log(` → 폴리곤으로 처리:`, row.name);
|
||||
// console.log(` → 폴리곤으로 처리:`, row.name);
|
||||
polygons.push({
|
||||
id: row.id || row.code || `polygon-${index}`,
|
||||
name: row.name || row.title || `영역 ${index + 1}`,
|
||||
|
|
@ -431,7 +431,7 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
|
|||
// 지역명으로 해상 구역 확인 (auto 또는 polygon 모드일 때만)
|
||||
const regionName = row.name || row.area || row.region || row.location || row.subRegion;
|
||||
if (regionName && MARITIME_ZONES[regionName] && mapDisplayType !== "marker") {
|
||||
console.log(` → 해상 구역 발견: ${regionName}, 폴리곤으로 처리`);
|
||||
// // console.log(` → 해상 구역 발견: ${regionName}, 폴리곤으로 처리`);
|
||||
polygons.push({
|
||||
id: `${sourceName}-polygon-${index}-${row.code || row.id || Date.now()}`, // 고유 ID 생성
|
||||
name: regionName,
|
||||
|
|
@ -451,24 +451,24 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
|
|||
// 위도/경도가 없으면 지역 코드/지역명으로 변환 시도
|
||||
if ((lat === undefined || lng === undefined) && (row.code || row.areaCode || row.regionCode || row.tmFc || row.stnId)) {
|
||||
const regionCode = row.code || row.areaCode || row.regionCode || row.tmFc || row.stnId;
|
||||
console.log(` → 지역 코드 발견: ${regionCode}, 위도/경도 변환 시도`);
|
||||
// // console.log(` → 지역 코드 발견: ${regionCode}, 위도/경도 변환 시도`);
|
||||
const coords = getCoordinatesByRegionCode(regionCode);
|
||||
if (coords) {
|
||||
lat = coords.lat;
|
||||
lng = coords.lng;
|
||||
console.log(` → 변환 성공: (${lat}, ${lng})`);
|
||||
// console.log(` → 변환 성공: (${lat}, ${lng})`);
|
||||
}
|
||||
}
|
||||
|
||||
// 지역명으로도 시도
|
||||
if ((lat === undefined || lng === undefined) && (row.name || row.area || row.region || row.location)) {
|
||||
const regionName = row.name || row.area || row.region || row.location;
|
||||
console.log(` → 지역명 발견: ${regionName}, 위도/경도 변환 시도`);
|
||||
// // console.log(` → 지역명 발견: ${regionName}, 위도/경도 변환 시도`);
|
||||
const coords = getCoordinatesByRegionName(regionName);
|
||||
if (coords) {
|
||||
lat = coords.lat;
|
||||
lng = coords.lng;
|
||||
console.log(` → 변환 성공: (${lat}, ${lng})`);
|
||||
// console.log(` → 변환 성공: (${lat}, ${lng})`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -476,7 +476,7 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
|
|||
if (mapDisplayType === "polygon") {
|
||||
const regionName = row.name || row.subRegion || row.region || row.area;
|
||||
if (regionName) {
|
||||
console.log(` 🔷 강제 폴리곤 모드: ${regionName} → 폴리곤으로 추가 (GeoJSON 매칭)`);
|
||||
// console.log(` 🔷 강제 폴리곤 모드: ${regionName} → 폴리곤으로 추가 (GeoJSON 매칭)`);
|
||||
polygons.push({
|
||||
id: `${sourceName}-polygon-${index}-${row.code || row.id || Date.now()}`,
|
||||
name: regionName,
|
||||
|
|
@ -487,14 +487,14 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
|
|||
color: dataSource?.polygonColor || getColorByStatus(row.status || row.level),
|
||||
});
|
||||
} else {
|
||||
console.log(` ⚠️ 강제 폴리곤 모드지만 지역명 없음 - 스킵`);
|
||||
// console.log(` ⚠️ 강제 폴리곤 모드지만 지역명 없음 - 스킵`);
|
||||
}
|
||||
return; // 폴리곤으로 처리했으므로 마커로는 추가하지 않음
|
||||
}
|
||||
|
||||
// 위도/경도가 있고 marker 모드가 아니면 마커로 처리
|
||||
if (lat !== undefined && lng !== undefined && mapDisplayType !== "polygon") {
|
||||
console.log(` → 마커로 처리: (${lat}, ${lng})`);
|
||||
// // console.log(` → 마커로 처리: (${lat}, ${lng})`);
|
||||
markers.push({
|
||||
id: `${sourceName}-marker-${index}-${row.code || row.id || Date.now()}`, // 고유 ID 생성
|
||||
lat: Number(lat),
|
||||
|
|
@ -511,7 +511,7 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
|
|||
// 위도/경도가 없는 육지 지역 → 폴리곤으로 추가 (GeoJSON 매칭용)
|
||||
const regionName = row.name || row.subRegion || row.region || row.area;
|
||||
if (regionName) {
|
||||
console.log(` 📍 위도/경도 없지만 지역명 있음: ${regionName} → 폴리곤으로 추가 (GeoJSON 매칭)`);
|
||||
// console.log(` 📍 위도/경도 없지만 지역명 있음: ${regionName} → 폴리곤으로 추가 (GeoJSON 매칭)`);
|
||||
polygons.push({
|
||||
id: `${sourceName}-polygon-${index}-${row.code || row.id || Date.now()}`,
|
||||
name: regionName,
|
||||
|
|
@ -522,13 +522,13 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
|
|||
color: dataSource?.polygonColor || getColorByStatus(row.status || row.level),
|
||||
});
|
||||
} else {
|
||||
console.log(` ⚠️ 위도/경도 없고 지역명도 없음 - 스킵`);
|
||||
console.log(` 데이터:`, row);
|
||||
// console.log(` ⚠️ 위도/경도 없고 지역명도 없음 - 스킵`);
|
||||
// console.log(` 데이터:`, row);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`✅ ${sourceName}: 마커 ${markers.length}개, 폴리곤 ${polygons.length}개 변환 완료`);
|
||||
// // console.log(`✅ ${sourceName}: 마커 ${markers.length}개, 폴리곤 ${polygons.length}개 변환 완료`);
|
||||
return { markers, polygons };
|
||||
};
|
||||
|
||||
|
|
@ -757,7 +757,7 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
|
|||
try {
|
||||
const response = await fetch("/geojson/korea-municipalities.json");
|
||||
const data = await response.json();
|
||||
console.log("🗺️ GeoJSON 로드 완료:", data.features?.length, "개 시/군/구");
|
||||
// // console.log("🗺️ GeoJSON 로드 완료:", data.features?.length, "개 시/군/구");
|
||||
setGeoJsonData(data);
|
||||
} catch (err) {
|
||||
console.error("❌ GeoJSON 로드 실패:", err);
|
||||
|
|
@ -769,11 +769,11 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
|
|||
// 초기 로드
|
||||
useEffect(() => {
|
||||
const dataSources = element?.dataSources || element?.chartConfig?.dataSources;
|
||||
console.log("🔄 useEffect 트리거! dataSources:", dataSources);
|
||||
// // console.log("🔄 useEffect 트리거! dataSources:", dataSources);
|
||||
if (dataSources && dataSources.length > 0) {
|
||||
loadMultipleDataSources();
|
||||
} else {
|
||||
console.log("⚠️ dataSources가 없거나 비어있음");
|
||||
// // console.log("⚠️ dataSources가 없거나 비어있음");
|
||||
setMarkers([]);
|
||||
setPolygons([]);
|
||||
}
|
||||
|
|
@ -791,15 +791,15 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
|
|||
if (intervals.length === 0) return;
|
||||
|
||||
const minInterval = Math.min(...intervals);
|
||||
console.log(`⏱️ 자동 새로고침 설정: ${minInterval}초마다`);
|
||||
// // console.log(`⏱️ 자동 새로고침 설정: ${minInterval}초마다`);
|
||||
|
||||
const intervalId = setInterval(() => {
|
||||
console.log("🔄 자동 새로고침 실행");
|
||||
// // console.log("🔄 자동 새로고침 실행");
|
||||
loadMultipleDataSources();
|
||||
}, minInterval * 1000);
|
||||
|
||||
return () => {
|
||||
console.log("⏹️ 자동 새로고침 정리");
|
||||
// // console.log("⏹️ 자동 새로고침 정리");
|
||||
clearInterval(intervalId);
|
||||
};
|
||||
}, [dataSources, loadMultipleDataSources]);
|
||||
|
|
@ -876,11 +876,11 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
|
|||
{/* 폴리곤 렌더링 */}
|
||||
{/* GeoJSON 렌더링 (육지 지역 경계선) */}
|
||||
{(() => {
|
||||
console.log(`🗺️ GeoJSON 렌더링 조건 체크:`, {
|
||||
geoJsonData: !!geoJsonData,
|
||||
polygonsLength: polygons.length,
|
||||
polygonNames: polygons.map(p => p.name),
|
||||
});
|
||||
// console.log(`🗺️ GeoJSON 렌더링 조건 체크:`, {
|
||||
// geoJsonData: !!geoJsonData,
|
||||
// polygonsLength: polygons.length,
|
||||
// polygonNames: polygons.map(p => p.name),
|
||||
// });
|
||||
return null;
|
||||
})()}
|
||||
{geoJsonData && polygons.length > 0 ? (
|
||||
|
|
@ -897,31 +897,31 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
|
|||
|
||||
// 정확한 매칭
|
||||
if (p.name === sigName) {
|
||||
console.log(`✅ 정확 매칭: ${p.name} === ${sigName}`);
|
||||
// console.log(`✅ 정확 매칭: ${p.name} === ${sigName}`);
|
||||
return true;
|
||||
}
|
||||
if (p.name === ctpName) {
|
||||
console.log(`✅ 정확 매칭: ${p.name} === ${ctpName}`);
|
||||
// console.log(`✅ 정확 매칭: ${p.name} === ${ctpName}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 부분 매칭 (GeoJSON 지역명에 폴리곤 이름이 포함되는지)
|
||||
if (sigName && sigName.includes(p.name)) {
|
||||
console.log(`✅ 부분 매칭: ${sigName} includes ${p.name}`);
|
||||
// console.log(`✅ 부분 매칭: ${sigName} includes ${p.name}`);
|
||||
return true;
|
||||
}
|
||||
if (ctpName && ctpName.includes(p.name)) {
|
||||
console.log(`✅ 부분 매칭: ${ctpName} includes ${p.name}`);
|
||||
// console.log(`✅ 부분 매칭: ${ctpName} includes ${p.name}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 역방향 매칭 (폴리곤 이름에 GeoJSON 지역명이 포함되는지)
|
||||
if (sigName && p.name.includes(sigName)) {
|
||||
console.log(`✅ 역방향 매칭: ${p.name} includes ${sigName}`);
|
||||
// console.log(`✅ 역방향 매칭: ${p.name} includes ${sigName}`);
|
||||
return true;
|
||||
}
|
||||
if (ctpName && p.name.includes(ctpName)) {
|
||||
console.log(`✅ 역방향 매칭: ${p.name} includes ${ctpName}`);
|
||||
// console.log(`✅ 역방향 매칭: ${p.name} includes ${ctpName}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -969,7 +969,9 @@ export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
|
|||
}}
|
||||
/>
|
||||
) : (
|
||||
<>{console.log(`⚠️ GeoJSON 렌더링 안 됨: geoJsonData=${!!geoJsonData}, polygons=${polygons.length}`)}</>
|
||||
<>
|
||||
{/* console.log(`⚠️ GeoJSON 렌더링 안 됨: geoJsonData=${!!geoJsonData}, polygons=${polygons.length}`) */}
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 폴리곤 렌더링 (해상 구역만) */}
|
||||
|
|
|
|||
|
|
@ -39,12 +39,12 @@ export default function RiskAlertTestWidget({ element }: RiskAlertTestWidgetProp
|
|||
const parseTextData = (text: string): any[] => {
|
||||
// XML 형식 감지
|
||||
if (text.trim().startsWith("<?xml") || text.trim().startsWith("<result>")) {
|
||||
console.log("📄 XML 형식 데이터 감지");
|
||||
// console.log("📄 XML 형식 데이터 감지");
|
||||
return parseXmlData(text);
|
||||
}
|
||||
|
||||
// CSV 형식 (기상청 특보)
|
||||
console.log("📄 CSV 형식 데이터 감지");
|
||||
// console.log("📄 CSV 형식 데이터 감지");
|
||||
const lines = text.split("\n").filter((line) => {
|
||||
const trimmed = line.trim();
|
||||
return trimmed && !trimmed.startsWith("#") && trimmed !== "=";
|
||||
|
|
@ -98,7 +98,7 @@ export default function RiskAlertTestWidget({ element }: RiskAlertTestWidgetProp
|
|||
results.push(obj);
|
||||
}
|
||||
|
||||
console.log(`✅ XML 파싱 완료: ${results.length}개 레코드`);
|
||||
// console.log(`✅ XML 파싱 완료: ${results.length}개 레코드`);
|
||||
return results;
|
||||
} catch (error) {
|
||||
console.error("❌ XML 파싱 실패:", error);
|
||||
|
|
@ -107,14 +107,78 @@ export default function RiskAlertTestWidget({ element }: RiskAlertTestWidgetProp
|
|||
};
|
||||
|
||||
const loadRestApiData = useCallback(async (source: ChartDataSource) => {
|
||||
if (!source.endpoint) {
|
||||
// 🆕 외부 연결 ID가 있으면 먼저 외부 연결 정보를 가져옴
|
||||
let actualEndpoint = source.endpoint;
|
||||
let actualQueryParams = source.queryParams;
|
||||
let actualHeaders = source.headers;
|
||||
|
||||
if (source.externalConnectionId) {
|
||||
// console.log("🔗 외부 연결 ID 감지:", source.externalConnectionId);
|
||||
try {
|
||||
const { ExternalDbConnectionAPI } = await import("@/lib/api/externalDbConnection");
|
||||
const connection = await ExternalDbConnectionAPI.getApiConnectionById(Number(source.externalConnectionId));
|
||||
|
||||
if (connection) {
|
||||
// console.log("✅ 외부 연결 정보 가져옴:", connection);
|
||||
|
||||
// 전체 엔드포인트 URL 생성
|
||||
actualEndpoint = connection.endpoint_path
|
||||
? `${connection.base_url}${connection.endpoint_path}`
|
||||
: connection.base_url;
|
||||
|
||||
// console.log("📍 실제 엔드포인트:", actualEndpoint);
|
||||
|
||||
// 기본 헤더 적용
|
||||
const headers: any[] = [];
|
||||
if (connection.default_headers && Object.keys(connection.default_headers).length > 0) {
|
||||
Object.entries(connection.default_headers).forEach(([key, value]) => {
|
||||
headers.push({ key, value });
|
||||
});
|
||||
}
|
||||
|
||||
// 인증 정보 적용
|
||||
const queryParams: any[] = [];
|
||||
if (connection.auth_type && connection.auth_type !== "none" && connection.auth_config) {
|
||||
const authConfig = connection.auth_config;
|
||||
|
||||
if (connection.auth_type === "api-key") {
|
||||
if (authConfig.keyLocation === "header" && authConfig.keyName && authConfig.keyValue) {
|
||||
headers.push({ key: authConfig.keyName, value: authConfig.keyValue });
|
||||
// console.log("🔑 API Key 헤더 추가:", authConfig.keyName);
|
||||
} else if (authConfig.keyLocation === "query" && authConfig.keyName && authConfig.keyValue) {
|
||||
const actualKeyName = authConfig.keyName === "apiKey" ? "key" : authConfig.keyName;
|
||||
queryParams.push({ key: actualKeyName, value: authConfig.keyValue });
|
||||
// console.log("🔑 API Key 쿼리 파라미터 추가:", actualKeyName);
|
||||
}
|
||||
} else if (connection.auth_type === "bearer" && authConfig.token) {
|
||||
headers.push({ key: "Authorization", value: `Bearer ${authConfig.token}` });
|
||||
// console.log("🔑 Bearer Token 헤더 추가");
|
||||
} else if (connection.auth_type === "basic" && authConfig.username && authConfig.password) {
|
||||
const credentials = btoa(`${authConfig.username}:${authConfig.password}`);
|
||||
headers.push({ key: "Authorization", value: `Basic ${credentials}` });
|
||||
// console.log("🔑 Basic Auth 헤더 추가");
|
||||
}
|
||||
}
|
||||
|
||||
actualHeaders = headers;
|
||||
actualQueryParams = queryParams;
|
||||
|
||||
// console.log("✅ 최종 헤더:", actualHeaders);
|
||||
// console.log("✅ 최종 쿼리 파라미터:", actualQueryParams);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("❌ 외부 연결 정보 가져오기 실패:", err);
|
||||
}
|
||||
}
|
||||
|
||||
if (!actualEndpoint) {
|
||||
throw new Error("API endpoint가 없습니다.");
|
||||
}
|
||||
|
||||
// 쿼리 파라미터 처리
|
||||
const queryParamsObj: Record<string, string> = {};
|
||||
if (source.queryParams && Array.isArray(source.queryParams)) {
|
||||
source.queryParams.forEach((param) => {
|
||||
if (actualQueryParams && Array.isArray(actualQueryParams)) {
|
||||
actualQueryParams.forEach((param) => {
|
||||
if (param.key && param.value) {
|
||||
queryParamsObj[param.key] = param.value;
|
||||
}
|
||||
|
|
@ -123,34 +187,33 @@ export default function RiskAlertTestWidget({ element }: RiskAlertTestWidgetProp
|
|||
|
||||
// 헤더 처리
|
||||
const headersObj: Record<string, string> = {};
|
||||
if (source.headers && Array.isArray(source.headers)) {
|
||||
source.headers.forEach((header) => {
|
||||
if (actualHeaders && Array.isArray(actualHeaders)) {
|
||||
actualHeaders.forEach((header) => {
|
||||
if (header.key && header.value) {
|
||||
headersObj[header.key] = header.value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
console.log("🌐 API 호출 준비:", {
|
||||
endpoint: source.endpoint,
|
||||
queryParams: queryParamsObj,
|
||||
headers: headersObj,
|
||||
});
|
||||
console.log("🔍 원본 source.queryParams:", source.queryParams);
|
||||
console.log("🔍 원본 source.headers:", source.headers);
|
||||
// console.log("🌐 API 호출 준비:", {
|
||||
// endpoint: actualEndpoint,
|
||||
// queryParams: queryParamsObj,
|
||||
// headers: headersObj,
|
||||
// });
|
||||
|
||||
const response = await fetch(getApiUrl("/api/dashboards/fetch-external-api"), {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
credentials: "include",
|
||||
body: JSON.stringify({
|
||||
url: source.endpoint,
|
||||
url: actualEndpoint,
|
||||
method: "GET",
|
||||
headers: headersObj,
|
||||
queryParams: queryParamsObj,
|
||||
}),
|
||||
});
|
||||
|
||||
console.log("🌐 API 응답 상태:", response.status);
|
||||
// console.log("🌐 API 응답 상태:", response.status);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}`);
|
||||
|
|
@ -163,22 +226,22 @@ export default function RiskAlertTestWidget({ element }: RiskAlertTestWidgetProp
|
|||
|
||||
let apiData = result.data;
|
||||
|
||||
console.log("🔍 API 응답 데이터 타입:", typeof apiData);
|
||||
console.log("🔍 API 응답 데이터 (처음 500자):", typeof apiData === "string" ? apiData.substring(0, 500) : JSON.stringify(apiData).substring(0, 500));
|
||||
// console.log("🔍 API 응답 데이터 타입:", typeof apiData);
|
||||
// console.log("🔍 API 응답 데이터 (처음 500자):", typeof apiData === "string" ? apiData.substring(0, 500) : JSON.stringify(apiData).substring(0, 500));
|
||||
|
||||
// 백엔드가 {text: "XML..."} 형태로 감싼 경우 처리
|
||||
if (apiData && typeof apiData === "object" && apiData.text && typeof apiData.text === "string") {
|
||||
console.log("📦 백엔드가 text 필드로 감싼 데이터 감지");
|
||||
// console.log("📦 백엔드가 text 필드로 감싼 데이터 감지");
|
||||
apiData = parseTextData(apiData.text);
|
||||
console.log("✅ 파싱 성공:", apiData.length, "개 행");
|
||||
// console.log("✅ 파싱 성공:", apiData.length, "개 행");
|
||||
} else if (typeof apiData === "string") {
|
||||
console.log("📄 텍스트 형식 데이터 감지, 파싱 시도");
|
||||
// console.log("📄 텍스트 형식 데이터 감지, 파싱 시도");
|
||||
apiData = parseTextData(apiData);
|
||||
console.log("✅ 파싱 성공:", apiData.length, "개 행");
|
||||
// console.log("✅ 파싱 성공:", apiData.length, "개 행");
|
||||
} else if (Array.isArray(apiData)) {
|
||||
console.log("✅ 이미 배열 형태의 데이터입니다.");
|
||||
// console.log("✅ 이미 배열 형태의 데이터입니다.");
|
||||
} else {
|
||||
console.log("⚠️ 예상치 못한 데이터 형식입니다. 배열로 변환 시도.");
|
||||
// console.log("⚠️ 예상치 못한 데이터 형식입니다. 배열로 변환 시도.");
|
||||
apiData = [apiData];
|
||||
}
|
||||
|
||||
|
|
@ -220,7 +283,7 @@ export default function RiskAlertTestWidget({ element }: RiskAlertTestWidgetProp
|
|||
}, []);
|
||||
|
||||
const convertToAlerts = useCallback((rows: any[], sourceName: string): Alert[] => {
|
||||
console.log("🔄 convertToAlerts 호출:", rows.length, "개 행");
|
||||
// console.log("🔄 convertToAlerts 호출:", rows.length, "개 행");
|
||||
|
||||
return rows.map((row: any, index: number) => {
|
||||
// 타입 결정 (UTIC XML 기준)
|
||||
|
|
@ -325,7 +388,7 @@ export default function RiskAlertTestWidget({ element }: RiskAlertTestWidgetProp
|
|||
source: sourceName,
|
||||
};
|
||||
|
||||
console.log(` ✅ Alert ${index}:`, alert);
|
||||
// console.log(` ✅ Alert ${index}:`, alert);
|
||||
return alert;
|
||||
});
|
||||
}, []);
|
||||
|
|
@ -338,19 +401,19 @@ export default function RiskAlertTestWidget({ element }: RiskAlertTestWidgetProp
|
|||
setLoading(true);
|
||||
setError(null);
|
||||
|
||||
console.log("🔄 RiskAlertTestWidget 데이터 로딩 시작:", dataSources.length, "개 소스");
|
||||
// console.log("🔄 RiskAlertTestWidget 데이터 로딩 시작:", dataSources.length, "개 소스");
|
||||
|
||||
try {
|
||||
const results = await Promise.allSettled(
|
||||
dataSources.map(async (source, index) => {
|
||||
console.log(`📡 데이터 소스 ${index + 1} 로딩 중:`, source.name, source.type);
|
||||
// console.log(`📡 데이터 소스 ${index + 1} 로딩 중:`, source.name, source.type);
|
||||
if (source.type === "api") {
|
||||
const alerts = await loadRestApiData(source);
|
||||
console.log(`✅ 데이터 소스 ${index + 1} 완료:`, alerts.length, "개 알림");
|
||||
// console.log(`✅ 데이터 소스 ${index + 1} 완료:`, alerts.length, "개 알림");
|
||||
return alerts;
|
||||
} else {
|
||||
const alerts = await loadDatabaseData(source);
|
||||
console.log(`✅ 데이터 소스 ${index + 1} 완료:`, alerts.length, "개 알림");
|
||||
// console.log(`✅ 데이터 소스 ${index + 1} 완료:`, alerts.length, "개 알림");
|
||||
return alerts;
|
||||
}
|
||||
})
|
||||
|
|
@ -359,14 +422,14 @@ export default function RiskAlertTestWidget({ element }: RiskAlertTestWidgetProp
|
|||
const allAlerts: Alert[] = [];
|
||||
results.forEach((result, index) => {
|
||||
if (result.status === "fulfilled") {
|
||||
console.log(`✅ 결과 ${index + 1} 병합:`, result.value.length, "개 알림");
|
||||
// console.log(`✅ 결과 ${index + 1} 병합:`, result.value.length, "개 알림");
|
||||
allAlerts.push(...result.value);
|
||||
} else {
|
||||
console.error(`❌ 결과 ${index + 1} 실패:`, result.reason);
|
||||
}
|
||||
});
|
||||
|
||||
console.log("✅ 총", allAlerts.length, "개 알림 로딩 완료");
|
||||
// console.log("✅ 총", allAlerts.length, "개 알림 로딩 완료");
|
||||
allAlerts.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
|
||||
setAlerts(allAlerts);
|
||||
setLastRefreshTime(new Date());
|
||||
|
|
@ -380,7 +443,7 @@ export default function RiskAlertTestWidget({ element }: RiskAlertTestWidgetProp
|
|||
|
||||
// 수동 새로고침 핸들러
|
||||
const handleManualRefresh = useCallback(() => {
|
||||
console.log("🔄 수동 새로고침 버튼 클릭");
|
||||
// console.log("🔄 수동 새로고침 버튼 클릭");
|
||||
loadMultipleDataSources();
|
||||
}, [loadMultipleDataSources]);
|
||||
|
||||
|
|
@ -403,15 +466,15 @@ export default function RiskAlertTestWidget({ element }: RiskAlertTestWidgetProp
|
|||
if (intervals.length === 0) return;
|
||||
|
||||
const minInterval = Math.min(...intervals);
|
||||
console.log(`⏱️ 자동 새로고침 설정: ${minInterval}초마다`);
|
||||
// console.log(`⏱️ 자동 새로고침 설정: ${minInterval}초마다`);
|
||||
|
||||
const intervalId = setInterval(() => {
|
||||
console.log("🔄 자동 새로고침 실행");
|
||||
// console.log("🔄 자동 새로고침 실행");
|
||||
loadMultipleDataSources();
|
||||
}, minInterval * 1000);
|
||||
|
||||
return () => {
|
||||
console.log("⏹️ 자동 새로고침 정리");
|
||||
// console.log("⏹️ 자동 새로고침 정리");
|
||||
clearInterval(intervalId);
|
||||
};
|
||||
}, [dataSources, loadMultipleDataSources]);
|
||||
|
|
@ -516,7 +579,7 @@ export default function RiskAlertTestWidget({ element }: RiskAlertTestWidgetProp
|
|||
</div>
|
||||
|
||||
{/* 컨텐츠 */}
|
||||
<div className="flex-1 overflow-hidden p-2">
|
||||
<div className="flex flex-1 flex-col overflow-hidden p-2">
|
||||
<div className="mb-2 flex gap-1 overflow-x-auto">
|
||||
<Button
|
||||
variant={filter === "all" ? "default" : "outline"}
|
||||
|
|
@ -545,7 +608,7 @@ export default function RiskAlertTestWidget({ element }: RiskAlertTestWidgetProp
|
|||
})}
|
||||
</div>
|
||||
|
||||
<div className="flex-1 space-y-1.5 overflow-y-auto">
|
||||
<div className="flex-1 space-y-1.5 overflow-y-auto pr-1">
|
||||
{filteredAlerts.length === 0 ? (
|
||||
<div className="flex h-full items-center justify-center text-gray-500">
|
||||
<p className="text-sm">알림이 없습니다</p>
|
||||
|
|
|
|||
Loading…
Reference in New Issue