diff --git a/frontend/components/dashboard/widgets/CustomMetricTestWidget.tsx b/frontend/components/dashboard/widgets/CustomMetricTestWidget.tsx index 45459703..81dbc292 100644 --- a/frontend/components/dashboard/widgets/CustomMetricTestWidget.tsx +++ b/frontend/components/dashboard/widgets/CustomMetricTestWidget.tsx @@ -240,31 +240,77 @@ export default function CustomMetricTestWidget({ element }: CustomMetricTestWidg ๋ฌธ์ž์—ด: stringColumns, }); - // ๐Ÿ†• ๊ฐ ํ–‰์„ ๋ฉ”ํŠธ๋ฆญ ์นด๋“œ๋กœ ํ‘œ์‹œ (์ˆซ์ž ์ปฌ๋Ÿผ ์—ฌ๋ถ€์™€ ๊ด€๊ณ„์—†์ด) - console.log(`โœ… [${sourceName}] ๊ฐ ํ–‰์„ ๋ฉ”ํŠธ๋ฆญ ์นด๋“œ๋กœ ๋ณ€ํ™˜`); + // ๐Ÿ†• ์ž๋™ ์ง‘๊ณ„ ๋กœ์ง: ์ง‘๊ณ„ ์ปฌ๋Ÿผ ์ด๋ฆ„์œผ๋กœ ํŒ๋‹จ (count, ๊ฐœ์ˆ˜, sum, avg ๋“ฑ) + const isAggregated = numericColumns.some((col) => + /count|๊ฐœ์ˆ˜|sum|ํ•ฉ๊ณ„|avg|ํ‰๊ท |min|์ตœ์†Œ|max|์ตœ๋Œ€|total|์ „์ฒด/i.test(col) + ); - rows.forEach((row, index) => { - // ๋ผ๋ฒจ: ์ฒซ ๋ฒˆ์งธ ์ปฌ๋Ÿผ (๋ณดํ†ต ID๋‚˜ ์ด๋ฆ„) - const labelField = columns[0]; - const label = String(row[labelField] || `ํ•ญ๋ชฉ ${index + 1}`); + if (isAggregated && numericColumns.length > 0) { + // ์ง‘๊ณ„ ์ปฌ๋Ÿผ์ด ์žˆ์œผ๋ฉด ์ด๋ฏธ ์ง‘๊ณ„๋œ ๋ฐ์ดํ„ฐ๋กœ ํŒ๋‹จ (GROUP BY ๊ฒฐ๊ณผ) + console.log(`โœ… [${sourceName}] ์ง‘๊ณ„๋œ ๋ฐ์ดํ„ฐ๋กœ ํŒ๋‹จ (์ง‘๊ณ„ ์ปฌ๋Ÿผ ๋ฐœ๊ฒฌ: ${numericColumns.join(", ")})`); - // ๊ฐ’: ์ˆซ์ž ์ปฌ๋Ÿผ์ด ์žˆ์œผ๋ฉด ์‚ฌ์šฉ, ์—†์œผ๋ฉด ํ–‰ ๋ฒˆํ˜ธ - const valueField = numericColumns.length > 0 ? numericColumns[0] : null; - const value = valueField ? Number(row[valueField]) || 0 : index + 1; + rows.forEach((row, index) => { + // ๋ผ๋ฒจ: ์ฒซ ๋ฒˆ์งธ ๋ฌธ์ž์—ด ์ปฌ๋Ÿผ + const labelField = stringColumns[0] || columns[0]; + const label = String(row[labelField] || `ํ•ญ๋ชฉ ${index + 1}`); - console.log(` [${sourceName}] ๋ฉ”ํŠธ๋ฆญ: ${label} = ${value}`); + // ๊ฐ’: ์ฒซ ๋ฒˆ์งธ ์ˆซ์ž ์ปฌ๋Ÿผ + const valueField = numericColumns[0]; + const value = Number(row[valueField]) || 0; + console.log(` [${sourceName}] ๋ฉ”ํŠธ๋ฆญ: ${label} = ${value}`); + + allMetrics.push({ + label: label, + value: value, + field: valueField, + aggregation: "custom", + color: colors[allMetrics.length % colors.length], + sourceName: sourceName, + rawData: [row], + }); + }); + } else { + // ์ˆซ์ž ์ปฌ๋Ÿผ์ด ์—†์œผ๋ฉด ์ž๋™ ์ง‘๊ณ„ (๋งˆ์ง€๋ง‰ ์ปฌ๋Ÿผ ๊ธฐ์ค€) + console.log(`โœ… [${sourceName}] ์ž๋™ ์ง‘๊ณ„ ๋ชจ๋“œ (์ˆซ์ž ์ปฌ๋Ÿผ ์—†์Œ)`); + + // ๋งˆ์ง€๋ง‰ ์ปฌ๋Ÿผ์„ ์ง‘๊ณ„ ๊ธฐ์ค€์œผ๋กœ ์‚ฌ์šฉ + const aggregateField = columns[columns.length - 1]; + console.log(` [${sourceName}] ์ง‘๊ณ„ ๊ธฐ์ค€ ์ปฌ๋Ÿผ: ${aggregateField}`); + + // ํ•ด๋‹น ์ปฌ๋Ÿผ์˜ ๊ฐ’๋ณ„๋กœ ์นด์šดํŠธ + const countMap = new Map(); + rows.forEach((row) => { + const value = String(row[aggregateField] || "๊ธฐํƒ€"); + countMap.set(value, (countMap.get(value) || 0) + 1); + }); + + // ์นด์šดํŠธ ๊ฒฐ๊ณผ๋ฅผ ๋ฉ”ํŠธ๋ฆญ์œผ๋กœ ๋ณ€ํ™˜ + countMap.forEach((count, label) => { + console.log(` [${sourceName}] ์ž๋™ ์ง‘๊ณ„: ${label} = ${count}๊ฐœ`); + + allMetrics.push({ + label: label, + value: count, + field: aggregateField, + aggregation: "count", + color: colors[allMetrics.length % colors.length], + sourceName: sourceName, + rawData: rows.filter((row) => String(row[aggregateField]) === label), + }); + }); + + // ์ „์ฒด ๊ฐœ์ˆ˜๋„ ์ถ”๊ฐ€ allMetrics.push({ - label: label, - value: value, - field: valueField || labelField, - aggregation: "custom", + label: "์ „์ฒด", + value: rows.length, + field: "count", + aggregation: "count", color: colors[allMetrics.length % colors.length], sourceName: sourceName, - rawData: [row], // ๐Ÿ†• ํ•ด๋‹น ํ–‰๋งŒ ์ €์žฅ (์ƒ์„ธ๋ณด๊ธฐ์šฉ) - fullData: rows, // ๐Ÿ†• ์ „์ฒด ๋ฐ์ดํ„ฐ๋„ ์ €์žฅ + rawData: rows, }); - }); + } // ๐Ÿ†• ์ˆซ์ž ์ปฌ๋Ÿผ์ด ์—†์„ ๋•Œ์˜ ๊ธฐ์กด ๋กœ์ง์€ ์ฃผ์„ ์ฒ˜๋ฆฌ if (false) {