@dssp/dkpi 1.0.0-alpha.62 → 1.0.0-alpha.63

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/dist-client/components/kpi-lookup-chart.js +41 -66
  2. package/dist-client/components/kpi-lookup-chart.js.map +1 -1
  3. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js +10 -10
  4. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js.map +1 -1
  5. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js +10 -10
  6. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js.map +1 -1
  7. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js +1 -1
  8. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js.map +1 -1
  9. package/dist-client/pages/sv-project-completed-list.js +9 -8
  10. package/dist-client/pages/sv-project-completed-list.js.map +1 -1
  11. package/dist-client/pages/sv-project-detail.js +14 -14
  12. package/dist-client/pages/sv-project-detail.js.map +1 -1
  13. package/dist-client/pages/sv-project-list.js +6 -6
  14. package/dist-client/pages/sv-project-list.js.map +1 -1
  15. package/dist-client/tsconfig.tsbuildinfo +1 -1
  16. package/dist-client/viewparts/menu-tools.js +11 -12
  17. package/dist-client/viewparts/menu-tools.js.map +1 -1
  18. package/dist-server/scripts/propagate-parent-kpi-values.d.ts +4 -0
  19. package/dist-server/scripts/propagate-parent-kpi-values.js +369 -77
  20. package/dist-server/scripts/propagate-parent-kpi-values.js.map +1 -1
  21. package/dist-server/service/kpi-stat/kpi-stat-query.js +34 -32
  22. package/dist-server/service/kpi-stat/kpi-stat-query.js.map +1 -1
  23. package/dist-server/tsconfig.tsbuildinfo +1 -1
  24. package/package.json +3 -3
  25. package/recalculate-batch.sh +64 -0
  26. package/recalculate-projects-range.sh +98 -0
  27. package/schema.graphql +1 -1
@@ -164,6 +164,7 @@ let KpiLookupChart = class KpiLookupChart extends LitElement {
164
164
  }
165
165
  }
166
166
  drawChart(g, svg, grades, width, height, chartWidth) {
167
+ var _a;
167
168
  console.log('drawVerticalChart - Original grades:', grades);
168
169
  console.log('drawVerticalChart - Current value:', this.value);
169
170
  // grades를 범위로 변환 (minValue == maxValue인 경우, 이전 값부터 현재 값까지를 범위로 간주)
@@ -200,41 +201,57 @@ let KpiLookupChart = class KpiLookupChart extends LitElement {
200
201
  displayMax = Math.max(maxVal, this.value);
201
202
  }
202
203
  const yScale = d3.scaleLinear().domain([minVal, displayMax]).range([height, 0]).nice();
203
- // X축 스케일 (score 범위)
204
- const barWidth = width / grades.length;
204
+ // X축 스케일 (score 범위) - 실제 score 값에 비례하도록 설정
205
+ const scores = gradeRanges.map(g => g.score).filter(s => s !== undefined && s !== null);
206
+ const minScore = Math.min(...scores);
207
+ const maxScore = Math.max(...scores);
208
+ const xScale = d3.scaleLinear()
209
+ .domain([minScore, maxScore])
210
+ .range([0, width])
211
+ .nice();
205
212
  const isManyGrades = grades.length > 10; // 10개 이상이면 "많은 경우"로 간주
206
213
  // 각 grade별로 영역과 라인 그리기
207
214
  const isLastGrade = (idx) => idx === gradeRanges.length - 1;
208
215
  gradeRanges.forEach((grade, index) => {
216
+ var _a, _b, _c;
209
217
  // 마지막 grade는 "infinity"로 간주하고 차트 상단까지 표시
210
218
  const minValue = grade.actualMin;
211
219
  const maxValue = grade.actualMax;
212
220
  const effectiveMaxValue = isLastGrade(index) ? displayMax : maxValue;
213
221
  const yMin = yScale(minValue);
214
222
  const yMax = yScale(effectiveMaxValue);
215
- const xCenter = index * barWidth + barWidth / 2;
223
+ // X축 위치와 너비를 score 값에 비례하여 계산
224
+ const currentScore = (_a = grade.score) !== null && _a !== void 0 ? _a : 0;
225
+ const nextScore = index < gradeRanges.length - 1 ? ((_b = gradeRanges[index + 1].score) !== null && _b !== void 0 ? _b : currentScore) : maxScore;
226
+ const prevScore = index > 0 ? ((_c = gradeRanges[index - 1].score) !== null && _c !== void 0 ? _c : currentScore) : minScore;
227
+ const xLeft = index === 0 ? xScale(minScore) : xScale((prevScore + currentScore) / 2);
228
+ const xRight = isLastGrade(index) ? xScale(maxScore) : xScale((currentScore + nextScore) / 2);
229
+ const barWidth = xRight - xLeft;
230
+ const xCenter = (xLeft + xRight) / 2;
216
231
  // 배경 영역 (매우 연한 색상)
217
232
  g.append('rect')
218
- .attr('x', index * barWidth)
233
+ .attr('x', xLeft)
219
234
  .attr('y', yMax)
220
235
  .attr('width', barWidth)
221
236
  .attr('height', yMin - yMax)
222
237
  .attr('fill', grade.color || this.getDefaultColor(index))
223
238
  .attr('opacity', 0.1);
224
239
  // 수평선으로 grade 경계 표시
225
- // 하한선
226
- g.append('line')
227
- .attr('x1', index * barWidth)
228
- .attr('x2', (index + 1) * barWidth)
229
- .attr('y1', yMin)
230
- .attr('y2', yMin)
231
- .attr('stroke', grade.color || this.getDefaultColor(index))
232
- .attr('stroke-width', 2);
233
- // 상한선 (마지막 grade가 아닌 경우)
240
+ // 하한선 (마지막 grade가 아닌 경우만 - 마지막은 차트 하단 경계이므로 불필요)
234
241
  if (!isLastGrade(index)) {
235
242
  g.append('line')
236
- .attr('x1', index * barWidth)
237
- .attr('x2', (index + 1) * barWidth)
243
+ .attr('x1', xLeft)
244
+ .attr('x2', xRight)
245
+ .attr('y1', yMin)
246
+ .attr('y2', yMin)
247
+ .attr('stroke', grade.color || this.getDefaultColor(index))
248
+ .attr('stroke-width', 2);
249
+ }
250
+ // 상한선 (첫 번째와 마지막 grade 제외 - 첫 번째는 차트 상단 경계, 마지막은 infinity)
251
+ if (index !== 0 && !isLastGrade(index)) {
252
+ g.append('line')
253
+ .attr('x1', xLeft)
254
+ .attr('x2', xRight)
238
255
  .attr('y1', yMax)
239
256
  .attr('y2', yMax)
240
257
  .attr('stroke', grade.color || this.getDefaultColor(index))
@@ -282,44 +299,16 @@ let KpiLookupChart = class KpiLookupChart extends LitElement {
282
299
  .attr('y', -45)
283
300
  .attr('text-anchor', 'middle')
284
301
  .text('KPI Value');
285
- // X축 라인 그리기
286
- g.append('line')
287
- .attr('x1', 0)
288
- .attr('x2', width)
289
- .attr('y1', height)
290
- .attr('y2', height)
291
- .attr('stroke', '#999')
292
- .attr('stroke-width', 1);
293
- // X축 그리기 (Grade가 많은 경우 적절히 스킵)
294
- if (isManyGrades) {
295
- // Grade가 많은 경우: 적절한 간격으로 라벨 표시
296
- const tickInterval = Math.ceil(grades.length / 10); // 최대 10개 정도만 표시
297
- gradeRanges.forEach((grade, index) => {
298
- if (index % tickInterval === 0 || index === gradeRanges.length - 1) {
299
- const tickX = index * barWidth + barWidth / 2;
300
- let label = '';
301
- if (grade.score !== undefined && grade.score !== null) {
302
- label = `${grade.score.toFixed(1)}`;
303
- }
304
- else {
305
- // score가 없으면 범위 표시
306
- const gradeIdx = gradeRanges.indexOf(grade);
307
- label = isLastGrade(gradeIdx) ? `${grade.actualMin.toFixed(0)} 이상` : `${grade.actualMax.toFixed(0)}`;
308
- }
309
- g.append('text')
310
- .attr('class', 'axis-label')
311
- .attr('x', tickX)
312
- .attr('y', height + 20)
313
- .attr('text-anchor', 'middle')
314
- .text(label);
315
- }
316
- });
317
- }
302
+ // X축 그리기 - d3 axis 사용
303
+ const xAxis = d3.axisBottom(xScale).ticks(10);
304
+ g.append('g')
305
+ .attr('transform', `translate(0,${height})`)
306
+ .call(xAxis);
318
307
  // X축 라벨 (KPI Score)
319
308
  g.append('text')
320
309
  .attr('class', 'axis-label')
321
310
  .attr('x', width / 2)
322
- .attr('y', height + 50)
311
+ .attr('y', height + 40)
323
312
  .attr('text-anchor', 'middle')
324
313
  .attr('font-weight', 'bold')
325
314
  .text('KPI Score');
@@ -336,10 +325,10 @@ let KpiLookupChart = class KpiLookupChart extends LitElement {
336
325
  // 범위를 찾은 경우에만 차트에 포인터 표시
337
326
  if (matchedGradeRange) {
338
327
  const valueY = yScale(this.value);
339
- const gradeIndex = gradeRanges.indexOf(matchedGradeRange);
340
- const valueX = gradeIndex * barWidth + barWidth / 2;
328
+ const matchedScore = (_a = matchedGradeRange.score) !== null && _a !== void 0 ? _a : 0;
329
+ const valueX = xScale(matchedScore);
341
330
  console.log('Current value:', this.value, 'valueY:', valueY);
342
- console.log('Grade index:', gradeIndex, 'valueX:', valueX);
331
+ console.log('Matched score:', matchedScore, 'valueX:', valueX);
343
332
  // 현재 값 포인터
344
333
  g.append('circle')
345
334
  .attr('cx', valueX)
@@ -348,20 +337,6 @@ let KpiLookupChart = class KpiLookupChart extends LitElement {
348
337
  .attr('fill', '#e53935')
349
338
  .attr('stroke', '#fff')
350
339
  .attr('stroke-width', 2);
351
- // Grade가 많은 경우: 현재 값의 grade 정보도 표시
352
- if (isManyGrades) {
353
- const matchedGradeIdx = gradeRanges.indexOf(matchedGradeRange);
354
- const rangeText = isLastGrade(matchedGradeIdx)
355
- ? `${matchedGradeRange.actualMin.toFixed(1)}${this.unit} 이상`
356
- : `${matchedGradeRange.actualMin.toFixed(1)}-${matchedGradeRange.actualMax.toFixed(1)}${this.unit}`;
357
- g.append('text')
358
- .attr('class', 'axis-label')
359
- .attr('x', valueX)
360
- .attr('y', height + 35)
361
- .attr('text-anchor', 'middle')
362
- .attr('fill', '#e53935')
363
- .text(rangeText);
364
- }
365
340
  }
366
341
  }
367
342
  }
@@ -1 +1 @@
1
- {"version":3,"file":"kpi-lookup-chart.js","sourceRoot":"","sources":["../../client/components/kpi-lookup-chart.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, html, css } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport * as d3 from 'd3'\n\ninterface Grade {\n name: string\n minValue: number\n maxValue: number\n score?: number // Optional to match KpiScore type\n color?: string\n description?: string // Optional field from KpiScore\n}\n\n@customElement('kpi-lookup-chart')\nexport class KpiLookupChart extends LitElement {\n @property({ type: Array }) grades: Grade[] = []\n @property({ type: Number }) value: number | null = null\n @property({ type: String }) unit: string = ''\n @property({ type: String }) kpiName: string = ''\n\n static styles = css`\n :host {\n display: block;\n width: 100%;\n height: 100%;\n min-height: 200px;\n }\n svg {\n width: 100%;\n height: 100%;\n display: block;\n }\n .chart-title {\n font-size: 14px;\n font-weight: 600;\n fill: #333;\n }\n .grade-label {\n font-size: 11px;\n fill: #666;\n }\n .value-label {\n font-size: 12px;\n font-weight: 600;\n fill: #e53935;\n }\n .axis-label {\n font-size: 11px;\n fill: #999;\n }\n `\n\n private chartWidth = 0\n private chartHeight = 0\n private resizeObserver?: ResizeObserver\n\n render() {\n return html`\n <svg\n id=\"lookup-chart\"\n width=${this.chartWidth}\n height=${this.chartHeight}\n viewBox=\"0 0 ${this.chartWidth} ${this.chartHeight}\"\n preserveAspectRatio=\"xMidYMid meet\"\n ></svg>\n `\n }\n\n connectedCallback() {\n super.connectedCallback()\n this.resizeObserver = new ResizeObserver(entries => {\n for (const entry of entries) {\n const rect = entry.contentRect\n this.chartWidth = rect.width\n this.chartHeight = rect.height\n this.requestUpdate()\n }\n })\n this.resizeObserver.observe(this)\n }\n\n disconnectedCallback() {\n this.resizeObserver?.disconnect()\n super.disconnectedCallback()\n }\n\n updated() {\n this.renderChart()\n }\n\n private renderChart() {\n const svg = d3.select(this.renderRoot.querySelector('#lookup-chart'))\n svg.selectAll('*').remove()\n\n // 데이터 검증\n if (!this.grades || this.grades.length === 0) {\n this.drawEmptyState(svg)\n return\n }\n\n const w = this.chartWidth || 400\n const h = this.chartHeight || 200\n const margin = { top: 40, right: 30, bottom: 50, left: 60 }\n const plotW = w - margin.left - margin.right\n const plotH = h - margin.top - margin.bottom\n\n const g = svg.append('g').attr('transform', `translate(${margin.left},${margin.top})`)\n\n // 제목\n if (this.kpiName) {\n svg\n .append('text')\n .attr('class', 'chart-title')\n .attr('x', w / 2)\n .attr('y', 15)\n .attr('text-anchor', 'middle')\n .text(this.kpiName)\n }\n\n // KPI Value와 Score를 차트 상단에 표시\n this.drawHeader(svg, w)\n\n // grades를 정렬 (minValue 기준)\n const sortedGrades = [...this.grades].sort((a, b) => a.minValue - b.minValue)\n\n this.drawChart(g, svg, sortedGrades, plotW, plotH, w)\n }\n\n private drawHeader(svg: any, chartWidth: number) {\n const headerY = 30\n\n // KPI Value 표시\n if (this.value !== null && this.value !== undefined) {\n svg\n .append('text')\n .attr('class', 'value-label')\n .attr('x', 10)\n .attr('y', headerY)\n .attr('font-size', '12px')\n .attr('font-weight', 'bold')\n .text(`KPI Value: ${this.value.toFixed(2)}${this.unit}`)\n } else {\n svg\n .append('text')\n .attr('class', 'value-label')\n .attr('x', 10)\n .attr('y', headerY)\n .attr('font-size', '12px')\n .attr('font-weight', 'bold')\n .attr('fill', '#999')\n .text(`KPI Value: N/A`)\n }\n\n // KPI Score 표시\n if (this.value !== null && this.value !== undefined) {\n // grades를 정렬\n const sortedGrades = [...this.grades].sort((a, b) => a.minValue - b.minValue)\n\n // grades를 범위로 변환\n const gradeRanges = sortedGrades.map((grade, index) => {\n let min: number, max: number\n if (grade.minValue === grade.maxValue) {\n min = index === 0 ? 0 : (sortedGrades[index - 1].maxValue || 0)\n max = grade.maxValue\n } else {\n min = grade.minValue\n max = grade.maxValue\n }\n return { ...grade, actualMin: min, actualMax: max }\n })\n\n // 마지막 grade인지 확인\n const isLastGrade = (idx: number) => idx === gradeRanges.length - 1\n\n // 해당 value가 속한 grade 찾기\n let matchedGradeRange = gradeRanges.find((g, idx) => {\n if (isLastGrade(idx)) {\n return this.value! >= g.actualMin\n }\n return this.value! >= g.actualMin && this.value! <= g.actualMax\n })\n\n const kpiScore = matchedGradeRange?.score\n\n // KPI Score 표시\n if (kpiScore !== undefined && kpiScore !== null) {\n svg\n .append('text')\n .attr('class', 'value-label')\n .attr('x', chartWidth / 2)\n .attr('y', headerY)\n .attr('font-size', '12px')\n .attr('font-weight', 'bold')\n .attr('text-anchor', 'middle')\n .text(`KPI Score: ${kpiScore.toFixed(2)}`)\n } else {\n svg\n .append('text')\n .attr('class', 'value-label')\n .attr('x', chartWidth / 2)\n .attr('y', headerY)\n .attr('font-size', '12px')\n .attr('font-weight', 'bold')\n .attr('text-anchor', 'middle')\n .attr('fill', '#ff0000')\n .text(`KPI Score: 범위를 찾을 수 없음`)\n }\n } else {\n // value가 없을 때 KPI Score N/A 표시\n svg\n .append('text')\n .attr('class', 'value-label')\n .attr('x', chartWidth / 2)\n .attr('y', headerY)\n .attr('font-size', '12px')\n .attr('font-weight', 'bold')\n .attr('text-anchor', 'middle')\n .attr('fill', '#999')\n .text(`KPI Score: N/A`)\n }\n }\n\n private drawChart(g: any, svg: any, grades: Grade[], width: number, height: number, chartWidth: number) {\n console.log('drawVerticalChart - Original grades:', grades)\n console.log('drawVerticalChart - Current value:', this.value)\n\n // grades를 범위로 변환 (minValue == maxValue인 경우, 이전 값부터 현재 값까지를 범위로 간주)\n const gradeRanges = grades.map((grade, index) => {\n let min: number, max: number\n\n if (grade.minValue === grade.maxValue) {\n // Point 값인 경우: 이전 grade의 max ~ 현재 grade의 value\n min = index === 0 ? 0 : (grades[index - 1].maxValue || 0)\n max = grade.maxValue\n } else {\n // Range 값인 경우: 그대로 사용\n min = grade.minValue\n max = grade.maxValue\n }\n\n return { ...grade, actualMin: min, actualMax: max }\n })\n\n console.log('drawVerticalChart - Grade ranges:', gradeRanges)\n\n // Y축 스케일 (value 범위)\n // 마지막 grade를 제외한 모든 값으로 범위 계산 (마지막 grade는 \"이상\"으로 표시)\n const allValues = gradeRanges\n .slice(0, -1) // 마지막 제외\n .flatMap(g => [g.actualMin, g.actualMax])\n\n // 마지막 grade의 min도 포함\n if (gradeRanges.length > 0) {\n allValues.push(gradeRanges[gradeRanges.length - 1].actualMin)\n }\n\n const minVal = allValues.length > 0 ? Math.min(...allValues, 0) : 0\n const maxVal = allValues.length > 0 ? Math.max(...allValues) : 100\n\n console.log('drawVerticalChart - Scaled min/max:', minVal, maxVal)\n\n // 현재 value가 있으면 범위에 포함\n let displayMax = maxVal\n if (this.value !== null && this.value !== undefined) {\n displayMax = Math.max(maxVal, this.value)\n }\n\n const yScale = d3.scaleLinear().domain([minVal, displayMax]).range([height, 0]).nice()\n\n // X축 스케일 (score 범위)\n const barWidth = width / grades.length\n const isManyGrades = grades.length > 10 // 10개 이상이면 \"많은 경우\"로 간주\n\n // 각 grade별로 영역과 라인 그리기\n const isLastGrade = (idx: number) => idx === gradeRanges.length - 1\n\n gradeRanges.forEach((grade, index) => {\n // 마지막 grade는 \"infinity\"로 간주하고 차트 상단까지 표시\n const minValue = grade.actualMin\n const maxValue = grade.actualMax\n const effectiveMaxValue = isLastGrade(index) ? displayMax : maxValue\n const yMin = yScale(minValue)\n const yMax = yScale(effectiveMaxValue)\n const xCenter = index * barWidth + barWidth / 2\n\n // 배경 영역 (매우 연한 색상)\n g.append('rect')\n .attr('x', index * barWidth)\n .attr('y', yMax)\n .attr('width', barWidth)\n .attr('height', yMin - yMax)\n .attr('fill', grade.color || this.getDefaultColor(index))\n .attr('opacity', 0.1)\n\n // 수평선으로 grade 경계 표시\n // 하한선\n g.append('line')\n .attr('x1', index * barWidth)\n .attr('x2', (index + 1) * barWidth)\n .attr('y1', yMin)\n .attr('y2', yMin)\n .attr('stroke', grade.color || this.getDefaultColor(index))\n .attr('stroke-width', 2)\n\n // 상한선 (마지막 grade가 아닌 경우)\n if (!isLastGrade(index)) {\n g.append('line')\n .attr('x1', index * barWidth)\n .attr('x2', (index + 1) * barWidth)\n .attr('y1', yMax)\n .attr('y2', yMax)\n .attr('stroke', grade.color || this.getDefaultColor(index))\n .attr('stroke-width', 2)\n .attr('stroke-dasharray', '3,3')\n }\n\n // Grade가 많은 경우: 라벨을 표시하지 않음\n if (!isManyGrades) {\n // Grade 라벨\n g.append('text')\n .attr('class', 'grade-label')\n .attr('x', xCenter)\n .attr('y', (yMin + yMax) / 2 + 4)\n .attr('text-anchor', 'middle')\n .text(grade.name)\n\n // Score 라벨 (score가 있을 경우만 표시)\n if (grade.score !== undefined && grade.score !== null) {\n g.append('text')\n .attr('class', 'grade-label')\n .attr('x', xCenter)\n .attr('y', height + 20)\n .attr('text-anchor', 'middle')\n .text(`Score: ${grade.score.toFixed(2)}`)\n }\n\n // Value range 라벨 (마지막 grade인 경우 \"이상\" 표시)\n const rangeText = isLastGrade(index)\n ? `${minValue.toFixed(1)}${this.unit} 이상`\n : `${minValue.toFixed(1)}-${maxValue.toFixed(1)}${this.unit}`\n\n g.append('text')\n .attr('class', 'axis-label')\n .attr('x', xCenter)\n .attr('y', height + 35)\n .attr('text-anchor', 'middle')\n .text(rangeText)\n }\n })\n\n // Y축 그리기\n const yAxis = d3.axisLeft(yScale).ticks(5)\n g.append('g').call(yAxis)\n\n // Y축 라벨 (KPI Value)\n g.append('text')\n .attr('class', 'axis-label')\n .attr('transform', 'rotate(-90)')\n .attr('x', -height / 2)\n .attr('y', -45)\n .attr('text-anchor', 'middle')\n .text('KPI Value')\n\n // X축 라인 그리기\n g.append('line')\n .attr('x1', 0)\n .attr('x2', width)\n .attr('y1', height)\n .attr('y2', height)\n .attr('stroke', '#999')\n .attr('stroke-width', 1)\n\n // X축 그리기 (Grade가 많은 경우 적절히 스킵)\n if (isManyGrades) {\n // Grade가 많은 경우: 적절한 간격으로 라벨 표시\n const tickInterval = Math.ceil(grades.length / 10) // 최대 10개 정도만 표시\n\n gradeRanges.forEach((grade, index) => {\n if (index % tickInterval === 0 || index === gradeRanges.length - 1) {\n const tickX = index * barWidth + barWidth / 2\n let label = ''\n\n if (grade.score !== undefined && grade.score !== null) {\n label = `${grade.score.toFixed(1)}`\n } else {\n // score가 없으면 범위 표시\n const gradeIdx = gradeRanges.indexOf(grade)\n label = isLastGrade(gradeIdx) ? `${grade.actualMin.toFixed(0)} 이상` : `${grade.actualMax.toFixed(0)}`\n }\n\n g.append('text')\n .attr('class', 'axis-label')\n .attr('x', tickX)\n .attr('y', height + 20)\n .attr('text-anchor', 'middle')\n .text(label)\n }\n })\n }\n\n // X축 라벨 (KPI Score)\n g.append('text')\n .attr('class', 'axis-label')\n .attr('x', width / 2)\n .attr('y', height + 50)\n .attr('text-anchor', 'middle')\n .attr('font-weight', 'bold')\n .text('KPI Score')\n\n // 현재 value가 있을 때만 차트에 포인터 표시\n if (this.value !== null && this.value !== undefined) {\n // 해당 value가 속한 grade 찾기 (actualMin, actualMax 사용)\n // 마지막 grade는 \"이상\"이므로 actualMin 이상만 체크\n let matchedGradeRange = gradeRanges.find((g, idx) => {\n if (isLastGrade(idx)) {\n return this.value! >= g.actualMin\n }\n return this.value! >= g.actualMin && this.value! <= g.actualMax\n })\n\n // 범위를 찾은 경우에만 차트에 포인터 표시\n if (matchedGradeRange) {\n const valueY = yScale(this.value)\n const gradeIndex = gradeRanges.indexOf(matchedGradeRange)\n const valueX = gradeIndex * barWidth + barWidth / 2\n\n console.log('Current value:', this.value, 'valueY:', valueY)\n console.log('Grade index:', gradeIndex, 'valueX:', valueX)\n\n // 현재 값 포인터\n g.append('circle')\n .attr('cx', valueX)\n .attr('cy', valueY)\n .attr('r', 6)\n .attr('fill', '#e53935')\n .attr('stroke', '#fff')\n .attr('stroke-width', 2)\n\n // Grade가 많은 경우: 현재 값의 grade 정보도 표시\n if (isManyGrades) {\n const matchedGradeIdx = gradeRanges.indexOf(matchedGradeRange)\n const rangeText = isLastGrade(matchedGradeIdx)\n ? `${matchedGradeRange.actualMin.toFixed(1)}${this.unit} 이상`\n : `${matchedGradeRange.actualMin.toFixed(1)}-${matchedGradeRange.actualMax.toFixed(1)}${this.unit}`\n\n g.append('text')\n .attr('class', 'axis-label')\n .attr('x', valueX)\n .attr('y', height + 35)\n .attr('text-anchor', 'middle')\n .attr('fill', '#e53935')\n .text(rangeText)\n }\n }\n }\n }\n\n private drawEmptyState(svg: any) {\n svg\n .append('text')\n .attr('x', this.chartWidth / 2)\n .attr('y', this.chartHeight / 2)\n .attr('text-anchor', 'middle')\n .attr('fill', '#999')\n .attr('font-size', '14px')\n .text('No grade data available')\n }\n\n private getDefaultColor(index: number): string {\n const colors = ['#4caf50', '#8bc34a', '#cddc39', '#ffeb3b', '#ffc107', '#ff9800', '#ff5722']\n return colors[index % colors.length]\n }\n}\n"]}
1
+ {"version":3,"file":"kpi-lookup-chart.js","sourceRoot":"","sources":["../../client/components/kpi-lookup-chart.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, html, css } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport * as d3 from 'd3'\n\ninterface Grade {\n name: string\n minValue: number\n maxValue: number\n score?: number // Optional to match KpiScore type\n color?: string\n description?: string // Optional field from KpiScore\n}\n\n@customElement('kpi-lookup-chart')\nexport class KpiLookupChart extends LitElement {\n @property({ type: Array }) grades: Grade[] = []\n @property({ type: Number }) value: number | null = null\n @property({ type: String }) unit: string = ''\n @property({ type: String }) kpiName: string = ''\n\n static styles = css`\n :host {\n display: block;\n width: 100%;\n height: 100%;\n min-height: 200px;\n }\n svg {\n width: 100%;\n height: 100%;\n display: block;\n }\n .chart-title {\n font-size: 14px;\n font-weight: 600;\n fill: #333;\n }\n .grade-label {\n font-size: 11px;\n fill: #666;\n }\n .value-label {\n font-size: 12px;\n font-weight: 600;\n fill: #e53935;\n }\n .axis-label {\n font-size: 11px;\n fill: #999;\n }\n `\n\n private chartWidth = 0\n private chartHeight = 0\n private resizeObserver?: ResizeObserver\n\n render() {\n return html`\n <svg\n id=\"lookup-chart\"\n width=${this.chartWidth}\n height=${this.chartHeight}\n viewBox=\"0 0 ${this.chartWidth} ${this.chartHeight}\"\n preserveAspectRatio=\"xMidYMid meet\"\n ></svg>\n `\n }\n\n connectedCallback() {\n super.connectedCallback()\n this.resizeObserver = new ResizeObserver(entries => {\n for (const entry of entries) {\n const rect = entry.contentRect\n this.chartWidth = rect.width\n this.chartHeight = rect.height\n this.requestUpdate()\n }\n })\n this.resizeObserver.observe(this)\n }\n\n disconnectedCallback() {\n this.resizeObserver?.disconnect()\n super.disconnectedCallback()\n }\n\n updated() {\n this.renderChart()\n }\n\n private renderChart() {\n const svg = d3.select(this.renderRoot.querySelector('#lookup-chart'))\n svg.selectAll('*').remove()\n\n // 데이터 검증\n if (!this.grades || this.grades.length === 0) {\n this.drawEmptyState(svg)\n return\n }\n\n const w = this.chartWidth || 400\n const h = this.chartHeight || 200\n const margin = { top: 40, right: 30, bottom: 50, left: 60 }\n const plotW = w - margin.left - margin.right\n const plotH = h - margin.top - margin.bottom\n\n const g = svg.append('g').attr('transform', `translate(${margin.left},${margin.top})`)\n\n // 제목\n if (this.kpiName) {\n svg\n .append('text')\n .attr('class', 'chart-title')\n .attr('x', w / 2)\n .attr('y', 15)\n .attr('text-anchor', 'middle')\n .text(this.kpiName)\n }\n\n // KPI Value와 Score를 차트 상단에 표시\n this.drawHeader(svg, w)\n\n // grades를 정렬 (minValue 기준)\n const sortedGrades = [...this.grades].sort((a, b) => a.minValue - b.minValue)\n\n this.drawChart(g, svg, sortedGrades, plotW, plotH, w)\n }\n\n private drawHeader(svg: any, chartWidth: number) {\n const headerY = 30\n\n // KPI Value 표시\n if (this.value !== null && this.value !== undefined) {\n svg\n .append('text')\n .attr('class', 'value-label')\n .attr('x', 10)\n .attr('y', headerY)\n .attr('font-size', '12px')\n .attr('font-weight', 'bold')\n .text(`KPI Value: ${this.value.toFixed(2)}${this.unit}`)\n } else {\n svg\n .append('text')\n .attr('class', 'value-label')\n .attr('x', 10)\n .attr('y', headerY)\n .attr('font-size', '12px')\n .attr('font-weight', 'bold')\n .attr('fill', '#999')\n .text(`KPI Value: N/A`)\n }\n\n // KPI Score 표시\n if (this.value !== null && this.value !== undefined) {\n // grades를 정렬\n const sortedGrades = [...this.grades].sort((a, b) => a.minValue - b.minValue)\n\n // grades를 범위로 변환\n const gradeRanges = sortedGrades.map((grade, index) => {\n let min: number, max: number\n if (grade.minValue === grade.maxValue) {\n min = index === 0 ? 0 : (sortedGrades[index - 1].maxValue || 0)\n max = grade.maxValue\n } else {\n min = grade.minValue\n max = grade.maxValue\n }\n return { ...grade, actualMin: min, actualMax: max }\n })\n\n // 마지막 grade인지 확인\n const isLastGrade = (idx: number) => idx === gradeRanges.length - 1\n\n // 해당 value가 속한 grade 찾기\n let matchedGradeRange = gradeRanges.find((g, idx) => {\n if (isLastGrade(idx)) {\n return this.value! >= g.actualMin\n }\n return this.value! >= g.actualMin && this.value! <= g.actualMax\n })\n\n const kpiScore = matchedGradeRange?.score\n\n // KPI Score 표시\n if (kpiScore !== undefined && kpiScore !== null) {\n svg\n .append('text')\n .attr('class', 'value-label')\n .attr('x', chartWidth / 2)\n .attr('y', headerY)\n .attr('font-size', '12px')\n .attr('font-weight', 'bold')\n .attr('text-anchor', 'middle')\n .text(`KPI Score: ${kpiScore.toFixed(2)}`)\n } else {\n svg\n .append('text')\n .attr('class', 'value-label')\n .attr('x', chartWidth / 2)\n .attr('y', headerY)\n .attr('font-size', '12px')\n .attr('font-weight', 'bold')\n .attr('text-anchor', 'middle')\n .attr('fill', '#ff0000')\n .text(`KPI Score: 범위를 찾을 수 없음`)\n }\n } else {\n // value가 없을 때 KPI Score N/A 표시\n svg\n .append('text')\n .attr('class', 'value-label')\n .attr('x', chartWidth / 2)\n .attr('y', headerY)\n .attr('font-size', '12px')\n .attr('font-weight', 'bold')\n .attr('text-anchor', 'middle')\n .attr('fill', '#999')\n .text(`KPI Score: N/A`)\n }\n }\n\n private drawChart(g: any, svg: any, grades: Grade[], width: number, height: number, chartWidth: number) {\n console.log('drawVerticalChart - Original grades:', grades)\n console.log('drawVerticalChart - Current value:', this.value)\n\n // grades를 범위로 변환 (minValue == maxValue인 경우, 이전 값부터 현재 값까지를 범위로 간주)\n const gradeRanges = grades.map((grade, index) => {\n let min: number, max: number\n\n if (grade.minValue === grade.maxValue) {\n // Point 값인 경우: 이전 grade의 max ~ 현재 grade의 value\n min = index === 0 ? 0 : (grades[index - 1].maxValue || 0)\n max = grade.maxValue\n } else {\n // Range 값인 경우: 그대로 사용\n min = grade.minValue\n max = grade.maxValue\n }\n\n return { ...grade, actualMin: min, actualMax: max }\n })\n\n console.log('drawVerticalChart - Grade ranges:', gradeRanges)\n\n // Y축 스케일 (value 범위)\n // 마지막 grade를 제외한 모든 값으로 범위 계산 (마지막 grade는 \"이상\"으로 표시)\n const allValues = gradeRanges\n .slice(0, -1) // 마지막 제외\n .flatMap(g => [g.actualMin, g.actualMax])\n\n // 마지막 grade의 min도 포함\n if (gradeRanges.length > 0) {\n allValues.push(gradeRanges[gradeRanges.length - 1].actualMin)\n }\n\n const minVal = allValues.length > 0 ? Math.min(...allValues, 0) : 0\n const maxVal = allValues.length > 0 ? Math.max(...allValues) : 100\n\n console.log('drawVerticalChart - Scaled min/max:', minVal, maxVal)\n\n // 현재 value가 있으면 범위에 포함\n let displayMax = maxVal\n if (this.value !== null && this.value !== undefined) {\n displayMax = Math.max(maxVal, this.value)\n }\n\n const yScale = d3.scaleLinear().domain([minVal, displayMax]).range([height, 0]).nice()\n\n // X축 스케일 (score 범위) - 실제 score 값에 비례하도록 설정\n const scores = gradeRanges.map(g => g.score).filter(s => s !== undefined && s !== null) as number[]\n const minScore = Math.min(...scores)\n const maxScore = Math.max(...scores)\n\n const xScale = d3.scaleLinear()\n .domain([minScore, maxScore])\n .range([0, width])\n .nice()\n\n const isManyGrades = grades.length > 10 // 10개 이상이면 \"많은 경우\"로 간주\n\n // 각 grade별로 영역과 라인 그리기\n const isLastGrade = (idx: number) => idx === gradeRanges.length - 1\n\n gradeRanges.forEach((grade, index) => {\n // 마지막 grade는 \"infinity\"로 간주하고 차트 상단까지 표시\n const minValue = grade.actualMin\n const maxValue = grade.actualMax\n const effectiveMaxValue = isLastGrade(index) ? displayMax : maxValue\n const yMin = yScale(minValue)\n const yMax = yScale(effectiveMaxValue)\n\n // X축 위치와 너비를 score 값에 비례하여 계산\n const currentScore = grade.score ?? 0\n const nextScore = index < gradeRanges.length - 1 ? (gradeRanges[index + 1].score ?? currentScore) : maxScore\n const prevScore = index > 0 ? (gradeRanges[index - 1].score ?? currentScore) : minScore\n\n const xLeft = index === 0 ? xScale(minScore) : xScale((prevScore + currentScore) / 2)\n const xRight = isLastGrade(index) ? xScale(maxScore) : xScale((currentScore + nextScore) / 2)\n const barWidth = xRight - xLeft\n const xCenter = (xLeft + xRight) / 2\n\n // 배경 영역 (매우 연한 색상)\n g.append('rect')\n .attr('x', xLeft)\n .attr('y', yMax)\n .attr('width', barWidth)\n .attr('height', yMin - yMax)\n .attr('fill', grade.color || this.getDefaultColor(index))\n .attr('opacity', 0.1)\n\n // 수평선으로 grade 경계 표시\n // 하한선 (마지막 grade가 아닌 경우만 - 마지막은 차트 하단 경계이므로 불필요)\n if (!isLastGrade(index)) {\n g.append('line')\n .attr('x1', xLeft)\n .attr('x2', xRight)\n .attr('y1', yMin)\n .attr('y2', yMin)\n .attr('stroke', grade.color || this.getDefaultColor(index))\n .attr('stroke-width', 2)\n }\n\n // 상한선 (첫 번째와 마지막 grade 제외 - 첫 번째는 차트 상단 경계, 마지막은 infinity)\n if (index !== 0 && !isLastGrade(index)) {\n g.append('line')\n .attr('x1', xLeft)\n .attr('x2', xRight)\n .attr('y1', yMax)\n .attr('y2', yMax)\n .attr('stroke', grade.color || this.getDefaultColor(index))\n .attr('stroke-width', 2)\n .attr('stroke-dasharray', '3,3')\n }\n\n // Grade가 많은 경우: 라벨을 표시하지 않음\n if (!isManyGrades) {\n // Grade 라벨\n g.append('text')\n .attr('class', 'grade-label')\n .attr('x', xCenter)\n .attr('y', (yMin + yMax) / 2 + 4)\n .attr('text-anchor', 'middle')\n .text(grade.name)\n\n // Score 라벨 (score가 있을 경우만 표시)\n if (grade.score !== undefined && grade.score !== null) {\n g.append('text')\n .attr('class', 'grade-label')\n .attr('x', xCenter)\n .attr('y', height + 20)\n .attr('text-anchor', 'middle')\n .text(`Score: ${grade.score.toFixed(2)}`)\n }\n\n // Value range 라벨 (마지막 grade인 경우 \"이상\" 표시)\n const rangeText = isLastGrade(index)\n ? `${minValue.toFixed(1)}${this.unit} 이상`\n : `${minValue.toFixed(1)}-${maxValue.toFixed(1)}${this.unit}`\n\n g.append('text')\n .attr('class', 'axis-label')\n .attr('x', xCenter)\n .attr('y', height + 35)\n .attr('text-anchor', 'middle')\n .text(rangeText)\n }\n })\n\n // Y축 그리기\n const yAxis = d3.axisLeft(yScale).ticks(5)\n g.append('g').call(yAxis)\n\n // Y축 라벨 (KPI Value)\n g.append('text')\n .attr('class', 'axis-label')\n .attr('transform', 'rotate(-90)')\n .attr('x', -height / 2)\n .attr('y', -45)\n .attr('text-anchor', 'middle')\n .text('KPI Value')\n\n // X축 그리기 - d3 axis 사용\n const xAxis = d3.axisBottom(xScale).ticks(10)\n g.append('g')\n .attr('transform', `translate(0,${height})`)\n .call(xAxis)\n\n // X축 라벨 (KPI Score)\n g.append('text')\n .attr('class', 'axis-label')\n .attr('x', width / 2)\n .attr('y', height + 40)\n .attr('text-anchor', 'middle')\n .attr('font-weight', 'bold')\n .text('KPI Score')\n\n // 현재 value가 있을 때만 차트에 포인터 표시\n if (this.value !== null && this.value !== undefined) {\n // 해당 value가 속한 grade 찾기 (actualMin, actualMax 사용)\n // 마지막 grade는 \"이상\"이므로 actualMin 이상만 체크\n let matchedGradeRange = gradeRanges.find((g, idx) => {\n if (isLastGrade(idx)) {\n return this.value! >= g.actualMin\n }\n return this.value! >= g.actualMin && this.value! <= g.actualMax\n })\n\n // 범위를 찾은 경우에만 차트에 포인터 표시\n if (matchedGradeRange) {\n const valueY = yScale(this.value)\n const matchedScore = matchedGradeRange.score ?? 0\n const valueX = xScale(matchedScore)\n\n console.log('Current value:', this.value, 'valueY:', valueY)\n console.log('Matched score:', matchedScore, 'valueX:', valueX)\n\n // 현재 값 포인터\n g.append('circle')\n .attr('cx', valueX)\n .attr('cy', valueY)\n .attr('r', 6)\n .attr('fill', '#e53935')\n .attr('stroke', '#fff')\n .attr('stroke-width', 2)\n }\n }\n }\n\n private drawEmptyState(svg: any) {\n svg\n .append('text')\n .attr('x', this.chartWidth / 2)\n .attr('y', this.chartHeight / 2)\n .attr('text-anchor', 'middle')\n .attr('fill', '#999')\n .attr('font-size', '14px')\n .text('No grade data available')\n }\n\n private getDefaultColor(index: number): string {\n const colors = ['#4caf50', '#8bc34a', '#cddc39', '#ffeb3b', '#ffc107', '#ff9800', '#ff5722']\n return colors[index % colors.length]\n }\n}\n"]}
@@ -195,7 +195,7 @@ let KpiLeftPanel = class KpiLeftPanel extends LitElement {
195
195
  const regionData = this.monthlyTrendData
196
196
  .filter(d => d.geoGroup === region)
197
197
  .sort((a, b) => a.yearMonth.localeCompare(b.yearMonth))
198
- .map(d => d.avgVal * 20); // 20배 스케일
198
+ .map(d => d.avgVal * 100); // 20배 스케일
199
199
  // 12개월 데이터가 없으면 빈 배열 반환
200
200
  return regionData.length > 0 ? regionData : [];
201
201
  }
@@ -239,7 +239,7 @@ let KpiLeftPanel = class KpiLeftPanel extends LitElement {
239
239
  data.push({
240
240
  org: '전체평균',
241
241
  category,
242
- value: Math.round(stat.avgVal * 20)
242
+ value: Math.round(stat.avgVal * 100)
243
243
  });
244
244
  });
245
245
  this.chartCategories = categories.length > 0 ? categories : ['일정', '비용', '품질', '안전', '환경', '생산성'];
@@ -258,13 +258,13 @@ let KpiLeftPanel = class KpiLeftPanel extends LitElement {
258
258
  }
259
259
  data.push({
260
260
  org: category,
261
- min: stat.minVal * 20,
262
- max: stat.maxVal * 20,
263
- q1: stat.q1Val * 20,
264
- q3: stat.q3Val * 20,
265
- median: stat.medVal * 20,
266
- mean: stat.avgVal * 20,
267
- value: stat.avgVal * 20
261
+ min: stat.minVal * 100,
262
+ max: stat.maxVal * 100,
263
+ q1: stat.q1Val * 100,
264
+ q3: stat.q3Val * 100,
265
+ median: stat.medVal * 100,
266
+ mean: stat.avgVal * 100,
267
+ value: stat.avgVal * 100
268
268
  });
269
269
  });
270
270
  this.chartCategories = categories.length > 0 ? categories : ['일정', '비용', '품질', '안전', '환경', '생산성'];
@@ -311,7 +311,7 @@ let KpiLeftPanel = class KpiLeftPanel extends LitElement {
311
311
  const changeRate = this.calculateChangeRate(regionName);
312
312
  return {
313
313
  region: regionName,
314
- kpi: stat ? (stat.avgVal * 20).toFixed(1) : '-',
314
+ kpi: stat ? (stat.avgVal * 100).toFixed(1) : '-',
315
315
  change: changeRate.toFixed(2),
316
316
  trendData: this.getRegionTrendData(regionName),
317
317
  lat: 36.5,
@@ -1 +1 @@
1
- {"version":3,"file":"kpi-left-panel.js","sourceRoot":"","sources":["../../../../client/pages/kpi-dashboard/components/kpi-left-panel.ts"],"names":[],"mappings":"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","sourcesContent":["import gql from 'graphql-tag'\nimport { LitElement, html, css } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { ScrollbarStyles } from '@operato/styles'\nimport { client } from '@operato/graphql'\n\nimport '../../../components/kpi-radar-chart.js'\nimport '../../../components/kpi-boxplot-chart.js'\nimport '../../../components/kpi-mini-trend-chart.js'\n\n@customElement('kpi-left-panel')\nexport class KpiLeftPanel extends LitElement {\n static styles = [\n ScrollbarStyles,\n css`\n :host {\n display: block;\n width: 400px;\n background: #fff;\n border-right: 1px solid #e0e0e0;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);\n }\n .panel-content {\n padding: 20px;\n overflow-y: auto;\n flex: 1;\n }\n .panel-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 20px;\n border-bottom: 1px solid #e0e0e0;\n background: #fff;\n height: 70px;\n box-sizing: border-box;\n }\n .panel-title {\n font-size: 1.3rem;\n font-weight: bold;\n color: #333;\n margin: 0;\n }\n .panel-close {\n width: 32px;\n height: 32px;\n border: none;\n background: #fff;\n border-radius: 50%;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 1.2rem;\n color: #666;\n transition: all 0.2s;\n }\n .panel-close:hover {\n background: #e9ecef;\n color: #333;\n }\n .sub-title {\n font-size: 1rem;\n font-weight: 600;\n margin-bottom: 16px;\n color: #495057;\n }\n .chart-section {\n background: #f8f9fa;\n border-radius: 8px;\n padding: 16px;\n margin-bottom: 20px;\n }\n .chart-toggle {\n display: flex;\n gap: 8px;\n margin-bottom: 16px;\n }\n .toggle-button {\n padding: 8px 16px;\n border: 1px solid #ced4da;\n background: #fff;\n border-radius: 6px;\n cursor: pointer;\n font-size: 0.9rem;\n transition: all 0.2s;\n }\n .toggle-button.active {\n background: #667eea;\n color: white;\n border-color: #667eea;\n }\n .chart-container {\n height: 300px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: white;\n border-radius: 6px;\n border: 1px solid #e9ecef;\n }\n .performance-table {\n width: 100%;\n border-collapse: collapse;\n margin-top: 16px;\n }\n .performance-table th,\n .performance-table td {\n padding: 12px 8px;\n text-align: left;\n border-bottom: 1px solid #e9ecef;\n }\n .performance-table th {\n background: #f8f9fa;\n font-weight: 600;\n color: #495057;\n }\n .performance-table td {\n color: #333;\n }\n .change-rate {\n display: flex;\n align-items: center;\n gap: 4px;\n }\n .change-up {\n color: #dc3545;\n }\n .change-down {\n color: #198754;\n }\n .change-neutral {\n color: #6c757d;\n }\n .trend-chart {\n width: 60px;\n height: 30px;\n background: #f8f9fa;\n border-radius: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 0.8rem;\n color: #666;\n }\n .download-button {\n margin-top: 16px;\n padding: 8px 16px;\n background: #28a745;\n color: white;\n border: none;\n border-radius: 6px;\n cursor: pointer;\n font-size: 0.9rem;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n .download-button:hover {\n background: #218838;\n }\n .category-select {\n width: 100%;\n padding: 8px 12px;\n border: 1px solid #ced4da;\n border-radius: 6px;\n background: white;\n margin-bottom: 20px;\n }\n `\n ]\n\n @property({ type: String }) selectedCategory = '전체 KPI'\n @property({ type: String }) selectedChartType = 'boxplot'\n @property({ type: Array }) mapData: any[] = []\n @property({ type: String }) startYearMonth = '' // YYYY-MM 형식\n @property({ type: String }) endYearMonth = '' // YYYY-MM 형식\n\n @state() private chartData: any[] = []\n @state() private chartCategories: string[] = []\n @state() private kpiYComprehensiveStats: any[] = []\n @state() private regionalKpiStats: any[] = []\n @state() private monthlyTrendData: any[] = []\n\n // 행정안전부 행정구역코드 순서\n private readonly regionOrder = [\n '서울특별시',\n '부산광역시',\n '대구광역시',\n '인천광역시',\n '광주광역시',\n '대전광역시',\n '울산광역시',\n '세종특별자치시',\n '경기도',\n '강원도',\n '충청북도',\n '충청남도',\n '전라북도',\n '전라남도',\n '경상북도',\n '경상남도',\n '제주특별자치도'\n ]\n\n connectedCallback() {\n super.connectedCallback()\n this.fetchKpiYComprehensiveStats()\n this.fetchRegionalKpiStats()\n this.fetchMonthlyTrendData()\n }\n\n updated(changedProperties: Map<string, any>) {\n // selectedCategory, startYearMonth, endYearMonth가 변경되면 데이터 다시 가져오기\n if (\n changedProperties.has('selectedCategory') ||\n changedProperties.has('startYearMonth') ||\n changedProperties.has('endYearMonth')\n ) {\n // 기간 정보가 설정되어 있을 때만 조회 (빈 문자열이면 전체 기간)\n if (this.startYearMonth !== undefined && this.endYearMonth !== undefined) {\n this.fetchKpiYComprehensiveStats()\n this.fetchRegionalKpiStats()\n this.fetchMonthlyTrendData()\n }\n }\n }\n\n async fetchKpiYComprehensiveStats() {\n try {\n const response = await client.query({\n query: gql`\n query GetKpiYValueComprehensiveStats($startYearMonth: String, $endYearMonth: String) {\n totalKpiYValueComprehensiveStats(startYearMonth: $startYearMonth, endYearMonth: $endYearMonth) {\n kpiName\n minVal\n q1Val\n medVal\n q3Val\n maxVal\n avgVal\n }\n }\n `,\n variables: {\n startYearMonth: this.startYearMonth,\n endYearMonth: this.endYearMonth\n }\n })\n\n this.kpiYComprehensiveStats = response.data.totalKpiYValueComprehensiveStats || []\n console.log('KPI Y-Value Comprehensive Stats:', this.kpiYComprehensiveStats)\n\n // 부모에게 전체 Y-level KPI 통계 전달\n this.dispatchEvent(\n new CustomEvent('total-kpi-y-stats-loaded', {\n detail: { data: this.kpiYComprehensiveStats },\n bubbles: true,\n composed: true\n })\n )\n\n this.generateChartData()\n } catch (error) {\n console.error('Failed to fetch KPI Y comprehensive stats:', error)\n this.kpiYComprehensiveStats = []\n this.generateChartData()\n }\n }\n\n async fetchRegionalKpiStats() {\n try {\n // selectedCategory에 따라 kpiName 결정\n const categoryToKpiName: Record<string, string | null> = {\n '전체 KPI': null,\n '일정 성과': 'Y1. 일정성과',\n '비용 성과': 'Y2. 비용성과',\n '품질 성과': 'Y3. 품질성과',\n '안전 성과': 'Y4. 안전성과',\n '환경 성과': 'Y5. 환경성과',\n '생산성 성과': 'Y6. 생산성성과'\n }\n\n const kpiName = categoryToKpiName[this.selectedCategory]\n\n const response = await client.query({\n query: gql`\n query GetRegionalKpiStats($kpiName: String, $startYearMonth: String, $endYearMonth: String) {\n kpiZValueComprehensiveStatsByGeoGroup(\n kpiName: $kpiName\n startYearMonth: $startYearMonth\n endYearMonth: $endYearMonth\n ) {\n geoGroup\n avgVal\n minVal\n maxVal\n projectCount\n }\n }\n `,\n variables: {\n kpiName,\n startYearMonth: this.startYearMonth,\n endYearMonth: this.endYearMonth\n }\n })\n\n this.regionalKpiStats = response.data.kpiZValueComprehensiveStatsByGeoGroup || []\n console.log('Regional KPI Stats:', this.regionalKpiStats)\n\n // 부모에게 지역별 KPI 통계 전달\n this.dispatchEvent(\n new CustomEvent('regional-kpi-stats-loaded', {\n detail: { data: this.regionalKpiStats },\n bubbles: true,\n composed: true\n })\n )\n } catch (error) {\n console.error('Failed to fetch regional KPI stats:', error)\n this.regionalKpiStats = []\n }\n }\n\n async fetchMonthlyTrendData() {\n try {\n // selectedCategory에 따라 kpiName 결정\n const categoryToKpiName: Record<string, string | null> = {\n '전체 KPI': null,\n '일정 성과': 'Y1. 일정성과',\n '비용 성과': 'Y2. 비용성과',\n '품질 성과': 'Y3. 품질성과',\n '안전 성과': 'Y4. 안전성과',\n '환경 성과': 'Y5. 환경성과',\n '생산성 성과': 'Y6. 생산성성과'\n }\n\n const kpiName = categoryToKpiName[this.selectedCategory]\n\n const response = await client.query({\n query: gql`\n query GetMonthlyTrendData($kpiName: String, $startYearMonth: String, $endYearMonth: String) {\n kpiZValueMonthlyTrendByGeoGroup(kpiName: $kpiName, startYearMonth: $startYearMonth, endYearMonth: $endYearMonth) {\n geoGroup\n yearMonth\n avgVal\n projectCount\n }\n }\n `,\n variables: {\n kpiName,\n startYearMonth: this.startYearMonth,\n endYearMonth: this.endYearMonth\n }\n })\n\n this.monthlyTrendData = response.data.kpiZValueMonthlyTrendByGeoGroup || []\n console.log('Monthly Trend Data:', this.monthlyTrendData)\n\n // 부모에게 월별 추이 데이터 전달\n this.dispatchEvent(\n new CustomEvent('monthly-trend-data-loaded', {\n detail: { data: this.monthlyTrendData },\n bubbles: true,\n composed: true\n })\n )\n } catch (error) {\n console.error('Failed to fetch monthly trend data:', error)\n this.monthlyTrendData = []\n }\n }\n\n private getRegionTrendData(region: string): number[] {\n // 해당 지역의 최근 12개월 트렌드 데이터 추출\n const regionData = this.monthlyTrendData\n .filter(d => d.geoGroup === region)\n .sort((a, b) => a.yearMonth.localeCompare(b.yearMonth))\n .map(d => d.avgVal * 20) // 20배 스케일\n\n // 12개월 데이터가 없으면 빈 배열 반환\n return regionData.length > 0 ? regionData : []\n }\n\n private calculateChangeRate(region: string): number {\n // 해당 지역의 월별 데이터를 시간순으로 정렬\n const regionData = this.monthlyTrendData\n .filter(d => d.geoGroup === region)\n .sort((a, b) => a.yearMonth.localeCompare(b.yearMonth))\n\n if (regionData.length < 2) return 0\n\n // 가장 오래된 달과 가장 최근 달의 값 비교\n const oldestValue = regionData[0].avgVal\n const latestValue = regionData[regionData.length - 1].avgVal\n\n if (oldestValue === 0) return 0\n\n // 변동율 계산: (최근값 - 과거값) / 과거값 * 100\n return ((latestValue - oldestValue) / oldestValue) * 100\n }\n\n private generateChartData() {\n // KPI 이름을 카테고리로 매핑 및 축약\n const categoryMapping: Record<string, string> = {\n 'Y1. 일정성과': '일정',\n 'Y2. 비용성과': '비용',\n 'Y3. 품질성과': '품질',\n 'Y4. 안전성과': '안전',\n 'Y5. 환경성과': '환경',\n 'Y6. 생산성성과': '생산성'\n }\n\n console.log('generateChartData - selectedChartType:', this.selectedChartType)\n console.log('generateChartData - kpiYComprehensiveStats:', this.kpiYComprehensiveStats)\n\n if (this.selectedChartType === 'radar') {\n // 레이더 차트용 데이터 생성 (전체 평균만 표시)\n const data: Array<{ org: string; category: string; value: number }> = []\n const categories: string[] = []\n\n this.kpiYComprehensiveStats.forEach(stat => {\n const category = categoryMapping[stat.kpiName] || stat.kpiName\n if (!categories.includes(category)) {\n categories.push(category)\n }\n\n // 전체 평균 데이터만 표시 (비교 시리즈 없음)\n data.push({\n org: '전체평균',\n category,\n value: Math.round(stat.avgVal * 20)\n })\n })\n\n this.chartCategories = categories.length > 0 ? categories : ['일정', '비용', '품질', '안전', '환경', '생산성']\n this.chartData = data\n console.log('Radar chart data:', data)\n console.log('Radar chart categories:', this.chartCategories)\n } else {\n // 박스플롯용 데이터 생성 (sv-project-detail.ts의 getYKpiBoxplotData 참조)\n const data: Array<{\n org: string\n min: number\n max: number\n q1: number\n q3: number\n median: number\n mean: number\n value: number\n }> = []\n const categories: string[] = []\n\n this.kpiYComprehensiveStats.forEach(stat => {\n const category = categoryMapping[stat.kpiName] || stat.kpiName\n if (!categories.includes(category)) {\n categories.push(category)\n }\n data.push({\n org: category,\n min: stat.minVal * 20,\n max: stat.maxVal * 20,\n q1: stat.q1Val * 20,\n q3: stat.q3Val * 20,\n median: stat.medVal * 20,\n mean: stat.avgVal * 20,\n value: stat.avgVal * 20\n })\n })\n\n this.chartCategories = categories.length > 0 ? categories : ['일정', '비용', '품질', '안전', '환경', '생산성']\n this.chartData = data\n console.log('Boxplot chart data:', data)\n console.log('Boxplot chart categories:', this.chartCategories)\n }\n }\n\n private onCategoryChange(event: Event) {\n const target = event.target as HTMLSelectElement\n this.selectedCategory = target.value\n\n // 카테고리 변경 시 지역별 통계와 월별 추이 데이터만 다시 가져오기\n // 종합 성과(박스플롯/레이더)는 항상 전체 Y-level KPI를 표시\n this.fetchRegionalKpiStats()\n this.fetchMonthlyTrendData()\n\n this.dispatchEvent(\n new CustomEvent('category-change', {\n detail: { category: target.value },\n bubbles: true,\n composed: true\n })\n )\n }\n\n private onChartTypeChange(type: string) {\n this.selectedChartType = type\n this.generateChartData()\n }\n\n private onRegionClick(region: string) {\n this.dispatchEvent(\n new CustomEvent('region-click', {\n detail: { region },\n bubbles: true,\n composed: true\n })\n )\n }\n\n private downloadExcel() {\n this.dispatchEvent(\n new CustomEvent('download-excel', {\n bubbles: true,\n composed: true\n })\n )\n }\n\n // regionOrder에 따라 지역 데이터를 정렬\n private getSortedRegionData() {\n // 실제 데이터를 기반으로 각 시도에 대한 데이터 생성\n return this.regionOrder.map(regionName => {\n const stat = this.regionalKpiStats.find(s => s.geoGroup === regionName)\n const changeRate = this.calculateChangeRate(regionName)\n return {\n region: regionName,\n kpi: stat ? (stat.avgVal * 20).toFixed(1) : '-',\n change: changeRate.toFixed(2),\n trendData: this.getRegionTrendData(regionName),\n lat: 36.5,\n lng: 127.5\n }\n })\n }\n\n private getChangeRateClass(change: number): string {\n if (change > 0) return 'change-up'\n if (change < 0) return 'change-down'\n return 'change-neutral'\n }\n\n private getChangeIcon(change: number): string {\n if (change > 0) return '▲'\n if (change < 0) return '▼'\n return '─'\n }\n\n render() {\n return html`\n <div class=\"panel-header\">\n <div class=\"panel-title\">전국 KPI</div>\n <button class=\"panel-close\" style=\"visibility: hidden;\">×</button>\n </div>\n <div class=\"panel-content\">\n <!-- KPI 카테고리 선택 -->\n <select class=\"category-select\" .value=${this.selectedCategory} @change=${this.onCategoryChange}>\n <option value=\"전체 KPI\">전체 KPI</option>\n <option value=\"일정 성과\">일정 성과</option>\n <option value=\"비용 성과\">비용 성과</option>\n <option value=\"품질 성과\">품질 성과</option>\n <option value=\"안전 성과\">안전 성과</option>\n <option value=\"환경 성과\">환경 성과</option>\n <option value=\"생산성 성과\">생산성 성과</option>\n </select>\n\n <!-- 종합 성과 -->\n <div class=\"chart-section\">\n <div class=\"sub-title\">종합 성과</div>\n <div class=\"chart-toggle\">\n <button\n class=\"toggle-button ${this.selectedChartType === 'boxplot' ? 'active' : ''}\"\n @click=${() => this.onChartTypeChange('boxplot')}\n >\n 박스플롯\n </button>\n <button\n class=\"toggle-button ${this.selectedChartType === 'radar' ? 'active' : ''}\"\n @click=${() => this.onChartTypeChange('radar')}\n >\n 레이더차트\n </button>\n </div>\n <div class=\"chart-container\">\n ${this.selectedChartType === 'boxplot'\n ? html` <sv-kpi-boxplot-chart .data=${this.chartData} .valueKey=${'value'}></sv-kpi-boxplot-chart> `\n : html`\n <sv-kpi-radar-chart\n .data=${this.chartData}\n .categories=${this.chartCategories}\n .valueKey=${'value'}\n ></sv-kpi-radar-chart>\n `}\n </div>\n </div>\n\n <!-- 시도별 성과 -->\n <div class=\"chart-section\">\n <div class=\"sub-title\">시도별 성과</div>\n <table class=\"performance-table\">\n <thead>\n <tr>\n <th>지역명</th>\n <th>KPI</th>\n <th>변동률(%)</th>\n <th>성과 추이</th>\n </tr>\n </thead>\n <tbody>\n ${this.getSortedRegionData().map(\n (item: any) => html`\n <tr\n style=\"cursor: pointer; transition: background-color 0.2s;\"\n @click=${() => this.onRegionClick(item.region)}\n @mouseenter=${(e: Event) => ((e.target as HTMLElement).style.backgroundColor = '#f8f9fa')}\n @mouseleave=${(e: Event) => ((e.target as HTMLElement).style.backgroundColor = '')}\n >\n <td>${item.region}</td>\n <td>${item.kpi}</td>\n <td>\n <div class=\"change-rate ${this.getChangeRateClass(item.change)}\">\n ${this.getChangeIcon(item.change)}${Math.abs(item.change)}%\n </div>\n </td>\n <td>\n ${item.trendData && item.trendData.length > 0\n ? html`\n <sv-kpi-mini-trend-chart\n .data=${item.trendData}\n .width=${60}\n .height=${30}\n .lineColor=${'#2196f3'}\n .strokeWidth=${1.5}\n .showPoints=${true}\n .pointRadius=${1.5}\n ></sv-kpi-mini-trend-chart>\n `\n : html`<span style=\"color: #999;\">-</span>`}\n </td>\n </tr>\n `\n )}\n </tbody>\n </table>\n <button class=\"download-button\" @click=${this.downloadExcel}>📊 엑셀 다운로드</button>\n </div>\n </div>\n `\n }\n}\n"]}
1
+ {"version":3,"file":"kpi-left-panel.js","sourceRoot":"","sources":["../../../../client/pages/kpi-dashboard/components/kpi-left-panel.ts"],"names":[],"mappings":"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","sourcesContent":["import gql from 'graphql-tag'\nimport { LitElement, html, css } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { ScrollbarStyles } from '@operato/styles'\nimport { client } from '@operato/graphql'\n\nimport '../../../components/kpi-radar-chart.js'\nimport '../../../components/kpi-boxplot-chart.js'\nimport '../../../components/kpi-mini-trend-chart.js'\n\n@customElement('kpi-left-panel')\nexport class KpiLeftPanel extends LitElement {\n static styles = [\n ScrollbarStyles,\n css`\n :host {\n display: block;\n width: 400px;\n background: #fff;\n border-right: 1px solid #e0e0e0;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);\n }\n .panel-content {\n padding: 20px;\n overflow-y: auto;\n flex: 1;\n }\n .panel-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 20px;\n border-bottom: 1px solid #e0e0e0;\n background: #fff;\n height: 70px;\n box-sizing: border-box;\n }\n .panel-title {\n font-size: 1.3rem;\n font-weight: bold;\n color: #333;\n margin: 0;\n }\n .panel-close {\n width: 32px;\n height: 32px;\n border: none;\n background: #fff;\n border-radius: 50%;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 1.2rem;\n color: #666;\n transition: all 0.2s;\n }\n .panel-close:hover {\n background: #e9ecef;\n color: #333;\n }\n .sub-title {\n font-size: 1rem;\n font-weight: 600;\n margin-bottom: 16px;\n color: #495057;\n }\n .chart-section {\n background: #f8f9fa;\n border-radius: 8px;\n padding: 16px;\n margin-bottom: 20px;\n }\n .chart-toggle {\n display: flex;\n gap: 8px;\n margin-bottom: 16px;\n }\n .toggle-button {\n padding: 8px 16px;\n border: 1px solid #ced4da;\n background: #fff;\n border-radius: 6px;\n cursor: pointer;\n font-size: 0.9rem;\n transition: all 0.2s;\n }\n .toggle-button.active {\n background: #667eea;\n color: white;\n border-color: #667eea;\n }\n .chart-container {\n height: 300px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: white;\n border-radius: 6px;\n border: 1px solid #e9ecef;\n }\n .performance-table {\n width: 100%;\n border-collapse: collapse;\n margin-top: 16px;\n }\n .performance-table th,\n .performance-table td {\n padding: 12px 8px;\n text-align: left;\n border-bottom: 1px solid #e9ecef;\n }\n .performance-table th {\n background: #f8f9fa;\n font-weight: 600;\n color: #495057;\n }\n .performance-table td {\n color: #333;\n }\n .change-rate {\n display: flex;\n align-items: center;\n gap: 4px;\n }\n .change-up {\n color: #dc3545;\n }\n .change-down {\n color: #198754;\n }\n .change-neutral {\n color: #6c757d;\n }\n .trend-chart {\n width: 60px;\n height: 30px;\n background: #f8f9fa;\n border-radius: 4px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 0.8rem;\n color: #666;\n }\n .download-button {\n margin-top: 16px;\n padding: 8px 16px;\n background: #28a745;\n color: white;\n border: none;\n border-radius: 6px;\n cursor: pointer;\n font-size: 0.9rem;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n .download-button:hover {\n background: #218838;\n }\n .category-select {\n width: 100%;\n padding: 8px 12px;\n border: 1px solid #ced4da;\n border-radius: 6px;\n background: white;\n margin-bottom: 20px;\n }\n `\n ]\n\n @property({ type: String }) selectedCategory = '전체 KPI'\n @property({ type: String }) selectedChartType = 'boxplot'\n @property({ type: Array }) mapData: any[] = []\n @property({ type: String }) startYearMonth = '' // YYYY-MM 형식\n @property({ type: String }) endYearMonth = '' // YYYY-MM 형식\n\n @state() private chartData: any[] = []\n @state() private chartCategories: string[] = []\n @state() private kpiYComprehensiveStats: any[] = []\n @state() private regionalKpiStats: any[] = []\n @state() private monthlyTrendData: any[] = []\n\n // 행정안전부 행정구역코드 순서\n private readonly regionOrder = [\n '서울특별시',\n '부산광역시',\n '대구광역시',\n '인천광역시',\n '광주광역시',\n '대전광역시',\n '울산광역시',\n '세종특별자치시',\n '경기도',\n '강원도',\n '충청북도',\n '충청남도',\n '전라북도',\n '전라남도',\n '경상북도',\n '경상남도',\n '제주특별자치도'\n ]\n\n connectedCallback() {\n super.connectedCallback()\n this.fetchKpiYComprehensiveStats()\n this.fetchRegionalKpiStats()\n this.fetchMonthlyTrendData()\n }\n\n updated(changedProperties: Map<string, any>) {\n // selectedCategory, startYearMonth, endYearMonth가 변경되면 데이터 다시 가져오기\n if (\n changedProperties.has('selectedCategory') ||\n changedProperties.has('startYearMonth') ||\n changedProperties.has('endYearMonth')\n ) {\n // 기간 정보가 설정되어 있을 때만 조회 (빈 문자열이면 전체 기간)\n if (this.startYearMonth !== undefined && this.endYearMonth !== undefined) {\n this.fetchKpiYComprehensiveStats()\n this.fetchRegionalKpiStats()\n this.fetchMonthlyTrendData()\n }\n }\n }\n\n async fetchKpiYComprehensiveStats() {\n try {\n const response = await client.query({\n query: gql`\n query GetKpiYValueComprehensiveStats($startYearMonth: String, $endYearMonth: String) {\n totalKpiYValueComprehensiveStats(startYearMonth: $startYearMonth, endYearMonth: $endYearMonth) {\n kpiName\n minVal\n q1Val\n medVal\n q3Val\n maxVal\n avgVal\n }\n }\n `,\n variables: {\n startYearMonth: this.startYearMonth,\n endYearMonth: this.endYearMonth\n }\n })\n\n this.kpiYComprehensiveStats = response.data.totalKpiYValueComprehensiveStats || []\n console.log('KPI Y-Value Comprehensive Stats:', this.kpiYComprehensiveStats)\n\n // 부모에게 전체 Y-level KPI 통계 전달\n this.dispatchEvent(\n new CustomEvent('total-kpi-y-stats-loaded', {\n detail: { data: this.kpiYComprehensiveStats },\n bubbles: true,\n composed: true\n })\n )\n\n this.generateChartData()\n } catch (error) {\n console.error('Failed to fetch KPI Y comprehensive stats:', error)\n this.kpiYComprehensiveStats = []\n this.generateChartData()\n }\n }\n\n async fetchRegionalKpiStats() {\n try {\n // selectedCategory에 따라 kpiName 결정\n const categoryToKpiName: Record<string, string | null> = {\n '전체 KPI': null,\n '일정 성과': 'Y1. 일정성과',\n '비용 성과': 'Y2. 비용성과',\n '품질 성과': 'Y3. 품질성과',\n '안전 성과': 'Y4. 안전성과',\n '환경 성과': 'Y5. 환경성과',\n '생산성 성과': 'Y6. 생산성성과'\n }\n\n const kpiName = categoryToKpiName[this.selectedCategory]\n\n const response = await client.query({\n query: gql`\n query GetRegionalKpiStats($kpiName: String, $startYearMonth: String, $endYearMonth: String) {\n kpiZValueComprehensiveStatsByGeoGroup(\n kpiName: $kpiName\n startYearMonth: $startYearMonth\n endYearMonth: $endYearMonth\n ) {\n geoGroup\n avgVal\n minVal\n maxVal\n projectCount\n }\n }\n `,\n variables: {\n kpiName,\n startYearMonth: this.startYearMonth,\n endYearMonth: this.endYearMonth\n }\n })\n\n this.regionalKpiStats = response.data.kpiZValueComprehensiveStatsByGeoGroup || []\n console.log('Regional KPI Stats:', this.regionalKpiStats)\n\n // 부모에게 지역별 KPI 통계 전달\n this.dispatchEvent(\n new CustomEvent('regional-kpi-stats-loaded', {\n detail: { data: this.regionalKpiStats },\n bubbles: true,\n composed: true\n })\n )\n } catch (error) {\n console.error('Failed to fetch regional KPI stats:', error)\n this.regionalKpiStats = []\n }\n }\n\n async fetchMonthlyTrendData() {\n try {\n // selectedCategory에 따라 kpiName 결정\n const categoryToKpiName: Record<string, string | null> = {\n '전체 KPI': null,\n '일정 성과': 'Y1. 일정성과',\n '비용 성과': 'Y2. 비용성과',\n '품질 성과': 'Y3. 품질성과',\n '안전 성과': 'Y4. 안전성과',\n '환경 성과': 'Y5. 환경성과',\n '생산성 성과': 'Y6. 생산성성과'\n }\n\n const kpiName = categoryToKpiName[this.selectedCategory]\n\n const response = await client.query({\n query: gql`\n query GetMonthlyTrendData($kpiName: String, $startYearMonth: String, $endYearMonth: String) {\n kpiZValueMonthlyTrendByGeoGroup(kpiName: $kpiName, startYearMonth: $startYearMonth, endYearMonth: $endYearMonth) {\n geoGroup\n yearMonth\n avgVal\n projectCount\n }\n }\n `,\n variables: {\n kpiName,\n startYearMonth: this.startYearMonth,\n endYearMonth: this.endYearMonth\n }\n })\n\n this.monthlyTrendData = response.data.kpiZValueMonthlyTrendByGeoGroup || []\n console.log('Monthly Trend Data:', this.monthlyTrendData)\n\n // 부모에게 월별 추이 데이터 전달\n this.dispatchEvent(\n new CustomEvent('monthly-trend-data-loaded', {\n detail: { data: this.monthlyTrendData },\n bubbles: true,\n composed: true\n })\n )\n } catch (error) {\n console.error('Failed to fetch monthly trend data:', error)\n this.monthlyTrendData = []\n }\n }\n\n private getRegionTrendData(region: string): number[] {\n // 해당 지역의 최근 12개월 트렌드 데이터 추출\n const regionData = this.monthlyTrendData\n .filter(d => d.geoGroup === region)\n .sort((a, b) => a.yearMonth.localeCompare(b.yearMonth))\n .map(d => d.avgVal * 100) // 20배 스케일\n\n // 12개월 데이터가 없으면 빈 배열 반환\n return regionData.length > 0 ? regionData : []\n }\n\n private calculateChangeRate(region: string): number {\n // 해당 지역의 월별 데이터를 시간순으로 정렬\n const regionData = this.monthlyTrendData\n .filter(d => d.geoGroup === region)\n .sort((a, b) => a.yearMonth.localeCompare(b.yearMonth))\n\n if (regionData.length < 2) return 0\n\n // 가장 오래된 달과 가장 최근 달의 값 비교\n const oldestValue = regionData[0].avgVal\n const latestValue = regionData[regionData.length - 1].avgVal\n\n if (oldestValue === 0) return 0\n\n // 변동율 계산: (최근값 - 과거값) / 과거값 * 100\n return ((latestValue - oldestValue) / oldestValue) * 100\n }\n\n private generateChartData() {\n // KPI 이름을 카테고리로 매핑 및 축약\n const categoryMapping: Record<string, string> = {\n 'Y1. 일정성과': '일정',\n 'Y2. 비용성과': '비용',\n 'Y3. 품질성과': '품질',\n 'Y4. 안전성과': '안전',\n 'Y5. 환경성과': '환경',\n 'Y6. 생산성성과': '생산성'\n }\n\n console.log('generateChartData - selectedChartType:', this.selectedChartType)\n console.log('generateChartData - kpiYComprehensiveStats:', this.kpiYComprehensiveStats)\n\n if (this.selectedChartType === 'radar') {\n // 레이더 차트용 데이터 생성 (전체 평균만 표시)\n const data: Array<{ org: string; category: string; value: number }> = []\n const categories: string[] = []\n\n this.kpiYComprehensiveStats.forEach(stat => {\n const category = categoryMapping[stat.kpiName] || stat.kpiName\n if (!categories.includes(category)) {\n categories.push(category)\n }\n\n // 전체 평균 데이터만 표시 (비교 시리즈 없음)\n data.push({\n org: '전체평균',\n category,\n value: Math.round(stat.avgVal * 100)\n })\n })\n\n this.chartCategories = categories.length > 0 ? categories : ['일정', '비용', '품질', '안전', '환경', '생산성']\n this.chartData = data\n console.log('Radar chart data:', data)\n console.log('Radar chart categories:', this.chartCategories)\n } else {\n // 박스플롯용 데이터 생성 (sv-project-detail.ts의 getYKpiBoxplotData 참조)\n const data: Array<{\n org: string\n min: number\n max: number\n q1: number\n q3: number\n median: number\n mean: number\n value: number\n }> = []\n const categories: string[] = []\n\n this.kpiYComprehensiveStats.forEach(stat => {\n const category = categoryMapping[stat.kpiName] || stat.kpiName\n if (!categories.includes(category)) {\n categories.push(category)\n }\n data.push({\n org: category,\n min: stat.minVal * 100,\n max: stat.maxVal * 100,\n q1: stat.q1Val * 100,\n q3: stat.q3Val * 100,\n median: stat.medVal * 100,\n mean: stat.avgVal * 100,\n value: stat.avgVal * 100\n })\n })\n\n this.chartCategories = categories.length > 0 ? categories : ['일정', '비용', '품질', '안전', '환경', '생산성']\n this.chartData = data\n console.log('Boxplot chart data:', data)\n console.log('Boxplot chart categories:', this.chartCategories)\n }\n }\n\n private onCategoryChange(event: Event) {\n const target = event.target as HTMLSelectElement\n this.selectedCategory = target.value\n\n // 카테고리 변경 시 지역별 통계와 월별 추이 데이터만 다시 가져오기\n // 종합 성과(박스플롯/레이더)는 항상 전체 Y-level KPI를 표시\n this.fetchRegionalKpiStats()\n this.fetchMonthlyTrendData()\n\n this.dispatchEvent(\n new CustomEvent('category-change', {\n detail: { category: target.value },\n bubbles: true,\n composed: true\n })\n )\n }\n\n private onChartTypeChange(type: string) {\n this.selectedChartType = type\n this.generateChartData()\n }\n\n private onRegionClick(region: string) {\n this.dispatchEvent(\n new CustomEvent('region-click', {\n detail: { region },\n bubbles: true,\n composed: true\n })\n )\n }\n\n private downloadExcel() {\n this.dispatchEvent(\n new CustomEvent('download-excel', {\n bubbles: true,\n composed: true\n })\n )\n }\n\n // regionOrder에 따라 지역 데이터를 정렬\n private getSortedRegionData() {\n // 실제 데이터를 기반으로 각 시도에 대한 데이터 생성\n return this.regionOrder.map(regionName => {\n const stat = this.regionalKpiStats.find(s => s.geoGroup === regionName)\n const changeRate = this.calculateChangeRate(regionName)\n return {\n region: regionName,\n kpi: stat ? (stat.avgVal * 100).toFixed(1) : '-',\n change: changeRate.toFixed(2),\n trendData: this.getRegionTrendData(regionName),\n lat: 36.5,\n lng: 127.5\n }\n })\n }\n\n private getChangeRateClass(change: number): string {\n if (change > 0) return 'change-up'\n if (change < 0) return 'change-down'\n return 'change-neutral'\n }\n\n private getChangeIcon(change: number): string {\n if (change > 0) return '▲'\n if (change < 0) return '▼'\n return '─'\n }\n\n render() {\n return html`\n <div class=\"panel-header\">\n <div class=\"panel-title\">전국 KPI</div>\n <button class=\"panel-close\" style=\"visibility: hidden;\">×</button>\n </div>\n <div class=\"panel-content\">\n <!-- KPI 카테고리 선택 -->\n <select class=\"category-select\" .value=${this.selectedCategory} @change=${this.onCategoryChange}>\n <option value=\"전체 KPI\">전체 KPI</option>\n <option value=\"일정 성과\">일정 성과</option>\n <option value=\"비용 성과\">비용 성과</option>\n <option value=\"품질 성과\">품질 성과</option>\n <option value=\"안전 성과\">안전 성과</option>\n <option value=\"환경 성과\">환경 성과</option>\n <option value=\"생산성 성과\">생산성 성과</option>\n </select>\n\n <!-- 종합 성과 -->\n <div class=\"chart-section\">\n <div class=\"sub-title\">종합 성과</div>\n <div class=\"chart-toggle\">\n <button\n class=\"toggle-button ${this.selectedChartType === 'boxplot' ? 'active' : ''}\"\n @click=${() => this.onChartTypeChange('boxplot')}\n >\n 박스플롯\n </button>\n <button\n class=\"toggle-button ${this.selectedChartType === 'radar' ? 'active' : ''}\"\n @click=${() => this.onChartTypeChange('radar')}\n >\n 레이더차트\n </button>\n </div>\n <div class=\"chart-container\">\n ${this.selectedChartType === 'boxplot'\n ? html` <sv-kpi-boxplot-chart .data=${this.chartData} .valueKey=${'value'}></sv-kpi-boxplot-chart> `\n : html`\n <sv-kpi-radar-chart\n .data=${this.chartData}\n .categories=${this.chartCategories}\n .valueKey=${'value'}\n ></sv-kpi-radar-chart>\n `}\n </div>\n </div>\n\n <!-- 시도별 성과 -->\n <div class=\"chart-section\">\n <div class=\"sub-title\">시도별 성과</div>\n <table class=\"performance-table\">\n <thead>\n <tr>\n <th>지역명</th>\n <th>KPI</th>\n <th>변동률(%)</th>\n <th>성과 추이</th>\n </tr>\n </thead>\n <tbody>\n ${this.getSortedRegionData().map(\n (item: any) => html`\n <tr\n style=\"cursor: pointer; transition: background-color 0.2s;\"\n @click=${() => this.onRegionClick(item.region)}\n @mouseenter=${(e: Event) => ((e.target as HTMLElement).style.backgroundColor = '#f8f9fa')}\n @mouseleave=${(e: Event) => ((e.target as HTMLElement).style.backgroundColor = '')}\n >\n <td>${item.region}</td>\n <td>${item.kpi}</td>\n <td>\n <div class=\"change-rate ${this.getChangeRateClass(item.change)}\">\n ${this.getChangeIcon(item.change)}${Math.abs(item.change)}%\n </div>\n </td>\n <td>\n ${item.trendData && item.trendData.length > 0\n ? html`\n <sv-kpi-mini-trend-chart\n .data=${item.trendData}\n .width=${60}\n .height=${30}\n .lineColor=${'#2196f3'}\n .strokeWidth=${1.5}\n .showPoints=${true}\n .pointRadius=${1.5}\n ></sv-kpi-mini-trend-chart>\n `\n : html`<span style=\"color: #999;\">-</span>`}\n </td>\n </tr>\n `\n )}\n </tbody>\n </table>\n <button class=\"download-button\" @click=${this.downloadExcel}>📊 엑셀 다운로드</button>\n </div>\n </div>\n `\n }\n}\n"]}
@@ -102,7 +102,7 @@ let KpiRegionPopup = class KpiRegionPopup extends LitElement {
102
102
  data.push({
103
103
  org: '전체평균',
104
104
  category,
105
- value: Math.round(stat.avgVal * 20)
105
+ value: Math.round(stat.avgVal * 100)
106
106
  });
107
107
  });
108
108
  // 지역 데이터 추가
@@ -111,7 +111,7 @@ let KpiRegionPopup = class KpiRegionPopup extends LitElement {
111
111
  data.push({
112
112
  org: this.selectedRegion || '지역',
113
113
  category,
114
- value: Math.round(stat.avgVal * 20)
114
+ value: Math.round(stat.avgVal * 100)
115
115
  });
116
116
  });
117
117
  this.chartCategories = categories.length > 0 ? categories : ['일정', '비용', '품질', '안전', '환경', '생산성'];
@@ -128,13 +128,13 @@ let KpiRegionPopup = class KpiRegionPopup extends LitElement {
128
128
  }
129
129
  data.push({
130
130
  org: category,
131
- min: stat.minVal * 20,
132
- max: stat.maxVal * 20,
133
- q1: stat.q1Val * 20,
134
- q3: stat.q3Val * 20,
135
- median: stat.medVal * 20,
136
- mean: stat.avgVal * 20,
137
- value: stat.avgVal * 20
131
+ min: stat.minVal * 100,
132
+ max: stat.maxVal * 100,
133
+ q1: stat.q1Val * 100,
134
+ q3: stat.q3Val * 100,
135
+ median: stat.medVal * 100,
136
+ mean: stat.avgVal * 100,
137
+ value: stat.avgVal * 100
138
138
  });
139
139
  });
140
140
  this.chartCategories = categories.length > 0 ? categories : ['일정', '비용', '품질', '안전', '환경', '생산성'];
@@ -149,7 +149,7 @@ let KpiRegionPopup = class KpiRegionPopup extends LitElement {
149
149
  .filter(d => d.geoGroup === this.selectedRegion)
150
150
  .sort((a, b) => a.yearMonth.localeCompare(b.yearMonth));
151
151
  this.trendData = regionTrendData.map(d => {
152
- const value = Math.round(d.avgVal * 20); // 20배 스케일
152
+ const value = Math.round(d.avgVal * 100); // 20배 스케일
153
153
  return {
154
154
  date: d.yearMonth, // YYYY-MM 형식
155
155
  value: value,