@dssp/dkpi 1.0.0-alpha.86 → 1.0.0-alpha.88

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 (65) hide show
  1. package/_index.html +4 -4
  2. package/assets/helps/index.md +1 -1
  3. package/assets/manifest.json +2 -2
  4. package/dist-client/components/kpi-boxplot-chart.d.ts +7 -0
  5. package/dist-client/components/kpi-boxplot-chart.js +20 -5
  6. package/dist-client/components/kpi-boxplot-chart.js.map +1 -1
  7. package/dist-client/components/kpi-lookup-chart.js +1 -17
  8. package/dist-client/components/kpi-lookup-chart.js.map +1 -1
  9. package/dist-client/components/kpi-radar-chart.js +2 -2
  10. package/dist-client/components/kpi-radar-chart.js.map +1 -1
  11. package/dist-client/components/kpi-single-boxplot-chart.d.ts +7 -0
  12. package/dist-client/components/kpi-single-boxplot-chart.js +27 -26
  13. package/dist-client/components/kpi-single-boxplot-chart.js.map +1 -1
  14. package/dist-client/components/kpi-step-lookup-chart.d.ts +33 -0
  15. package/dist-client/components/kpi-step-lookup-chart.js +181 -0
  16. package/dist-client/components/kpi-step-lookup-chart.js.map +1 -0
  17. package/dist-client/components/kpi-trend-chart.js +2 -12
  18. package/dist-client/components/kpi-trend-chart.js.map +1 -1
  19. package/dist-client/google-map/common-google-map.js +5 -1
  20. package/dist-client/google-map/common-google-map.js.map +1 -1
  21. package/dist-client/pages/kpi-admin/dssp-kpi-overview.d.ts +2 -1
  22. package/dist-client/pages/kpi-admin/dssp-kpi-overview.js +24 -9
  23. package/dist-client/pages/kpi-admin/dssp-kpi-overview.js.map +1 -1
  24. package/dist-client/pages/kpi-admin/kpi-system-guide.js +311 -158
  25. package/dist-client/pages/kpi-admin/kpi-system-guide.js.map +1 -1
  26. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.d.ts +6 -0
  27. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js +110 -50
  28. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js.map +1 -1
  29. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.d.ts +0 -9
  30. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js +6 -91
  31. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js.map +1 -1
  32. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.d.ts +2 -0
  33. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js +148 -68
  34. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js.map +1 -1
  35. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js +85 -41
  36. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js.map +1 -1
  37. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js +15 -7
  38. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js.map +1 -1
  39. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js +16 -25
  40. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js.map +1 -1
  41. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js +9 -13
  42. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js.map +1 -1
  43. package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.js +9 -15
  44. package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.js.map +1 -1
  45. package/dist-client/pages/sv-project-complete.js +7 -11
  46. package/dist-client/pages/sv-project-complete.js.map +1 -1
  47. package/dist-client/pages/sv-project-completed-list.js +4 -4
  48. package/dist-client/pages/sv-project-completed-list.js.map +1 -1
  49. package/dist-client/pages/sv-project-detail.d.ts +1 -0
  50. package/dist-client/pages/sv-project-detail.js +94 -82
  51. package/dist-client/pages/sv-project-detail.js.map +1 -1
  52. package/dist-client/pages/sv-project-list.js +15 -12
  53. package/dist-client/pages/sv-project-list.js.map +1 -1
  54. package/dist-client/tsconfig.tsbuildinfo +1 -1
  55. package/dist-client/viewparts/menu-tools.js +17 -3
  56. package/dist-client/viewparts/menu-tools.js.map +1 -1
  57. package/dist-server/service/kpi-stat/kpi-stat-query.js +20 -19
  58. package/dist-server/service/kpi-stat/kpi-stat-query.js.map +1 -1
  59. package/dist-server/tsconfig.tsbuildinfo +1 -1
  60. package/openapi/unstable.yaml +2 -2
  61. package/package.json +3 -3
  62. package/schema.graphql +20 -0
  63. package/translations/ko.json +2 -2
  64. package/views/auth-page.html +4 -4
  65. package/views/public/home.html +5 -5
@@ -13,6 +13,7 @@ import '../components/kpi-boxplot-chart';
13
13
  import '../components/kpi-single-boxplot-chart';
14
14
  import '../components/kpi-radar-chart';
15
15
  import '../components/kpi-lookup-chart';
16
+ import '../components/kpi-step-lookup-chart';
16
17
  import '../components/kpi-2d-lookup-chart';
17
18
  let SvProjectDetailPage = class SvProjectDetailPage extends ScopedElementsMixin(PageView) {
18
19
  constructor() {
@@ -31,13 +32,11 @@ let SvProjectDetailPage = class SvProjectDetailPage extends ScopedElementsMixin(
31
32
  get context() {
32
33
  var _a;
33
34
  return {
34
- title: !isProjectTypeDomain() && this.project
35
- ? `프로젝트 상세 정보 - ${(_a = this.project) === null || _a === void 0 ? void 0 : _a.name}`
36
- : '프로젝트 상세 정보'
35
+ title: !isProjectTypeDomain() && this.project ? `프로젝트 상세 정보 - ${(_a = this.project) === null || _a === void 0 ? void 0 : _a.name}` : '프로젝트 상세 정보'
37
36
  };
38
37
  }
39
38
  render() {
40
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
39
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
41
40
  return html `
42
41
  <div content>
43
42
  <div left>
@@ -59,7 +58,7 @@ let SvProjectDetailPage = class SvProjectDetailPage extends ScopedElementsMixin(
59
58
  <div class="value">${this.project.startDate} ~ ${this.project.endDate}</div>
60
59
  </div>
61
60
  <div class="row">
62
- <div class="label">• 현장상태</div>
61
+ <div class="label">• 프로젝트 상태</div>
63
62
  <div class="value status-value">${PROJECT_STATE[this.project.state]}</div>
64
63
  </div>
65
64
  <div class="row">
@@ -96,15 +95,11 @@ let SvProjectDetailPage = class SvProjectDetailPage extends ScopedElementsMixin(
96
95
  </div>
97
96
  <div class="grid-item row">
98
97
  <div class="label">• 용적률</div>
99
- <div class="value">
100
- ${((_h = (_g = this.project.buildingComplex) === null || _g === void 0 ? void 0 : _g.floorAreaRatio) === null || _h === void 0 ? void 0 : _h.toLocaleString()) || '-'} %
101
- </div>
98
+ <div class="value">${((_h = (_g = this.project.buildingComplex) === null || _g === void 0 ? void 0 : _g.floorAreaRatio) === null || _h === void 0 ? void 0 : _h.toLocaleString()) || '-'} %</div>
102
99
  </div>
103
100
  <div class="grid-item row">
104
- <div class="label">• 투입인력</div>
105
- <div class="value">
106
- ${((_k = (_j = this.project.buildingComplex) === null || _j === void 0 ? void 0 : _j.workerCount) === null || _k === void 0 ? void 0 : _k.toLocaleString()) || '-'} 명
107
- </div>
101
+ <div class="label">• 총 투입인력(M/M)</div>
102
+ <div class="value">${((_k = (_j = this.project.buildingComplex) === null || _j === void 0 ? void 0 : _j.workerCount) === null || _k === void 0 ? void 0 : _k.toLocaleString()) || '-'} 명</div>
108
103
  </div>
109
104
  </div>
110
105
  </div>
@@ -214,14 +209,13 @@ let SvProjectDetailPage = class SvProjectDetailPage extends ScopedElementsMixin(
214
209
  <div class="card group-box">
215
210
  <div class="kpi-header">
216
211
  <div>
217
- <div>그룹별 분포</div>
212
+ <div>성과영역별 KPI</div>
218
213
  <div class="triangle"></div>
219
214
  </div>
220
215
  </div>
221
216
  <div class="desc">
222
- Boxplot(박스플롯)은 카테고리별로 값의 분포(최소, 1사분위, 중앙값, 3사분위, 최대, 평균, 이상치 ) 보여줍니다.
223
- 박스는 중앙 50% 구간, 수염은 전체 범위, 굵은 검정색 가로선은 중앙값(메디안), 초록색 원은 평균값(Mean)을 의미합니다.
224
- 이 프로젝트의 값은 진한 오렌지색 원으로 별도 강조되어 표시되며, 중앙값/평균과 다를 수 있습니다.
217
+ Boxplot(박스플롯)은 박스는 중앙 50% 구간, 수염은 전체 범위, 굵은 파란색 가로선은 중앙값(median), 초록색 원은
218
+ 평균값(Mean)을 의미합니다. 당해 프로젝트의 값은 붉은색 원으로 표시됩니다.
225
219
  </div>
226
220
  <div class="group-chart">
227
221
  <sv-kpi-boxplot-chart .data=${this.getYKpiBoxplotData()}></sv-kpi-boxplot-chart>
@@ -231,7 +225,7 @@ let SvProjectDetailPage = class SvProjectDetailPage extends ScopedElementsMixin(
231
225
  <div class="metrics-card">
232
226
  <div class="metrics-header">
233
227
  <div class="title">
234
- <span>그룹별 상세 매트릭 데이터</span>
228
+ <span>성과영역별 KPI 측정값</span>
235
229
  <div class="triangle"></div>
236
230
  </div>
237
231
  </div>
@@ -276,38 +270,48 @@ let SvProjectDetailPage = class SvProjectDetailPage extends ScopedElementsMixin(
276
270
  ${this.selectedKpi.score !== undefined && this.selectedKpi.score !== null
277
271
  ? html `
278
272
  <div class="popup-score">
279
- <span class="score-value">${parseFloat(Number(this.selectedKpi.score).toFixed(4))}</span>
280
- <span class="score-label">점</span>
273
+ <span class="score-value">${parseFloat(Number(this.selectedKpi.score).toFixed(2))}</span>
281
274
  </div>
282
275
  `
283
276
  : ''}
284
277
  <button class="popup-close" @click=${this._closePopup}>×</button>
285
278
  </div>
286
279
  <div class="popup-chart-container">
287
- ${ /*
288
- * scoreType 기반 차트 분기:
289
- *
290
- * CUSTOM (X13 등): 2D 룩업 히트맵 — 단, 증강데이터에서는 value가
291
- * 이미 1~5 정수(룩업 결과)이므로 히트맵 대신 게이지로 폴백.
292
- * 실제 프로젝트에서는 value가 편차율(소수점)이므로 히트맵 표시.
293
- *
294
- * LOOKUP: 성과 분포 커브 (kpi-lookup-chart)
295
- * ASSESSMENT: 5단계 게이지 (kpi-lookup-chart 자동 감지)
296
- */''}
297
280
  ${
298
- // 2D lookup KPI (X13 등) 는 항상 등급 기준 + 히트맵 함께 표시 (value 가
299
- // 정수든 소수든 일관). _renderAssessmentGrades 안의 is2D 분기가 히트맵
300
- // 같이 그림. 그 외 ASSESSMENT 도 등급 기준, LOOKUP 은 분포 커브.
301
- (this.selectedKpi.scoreType === 'CUSTOM' ||
302
- ((_l = this.selectedKpi.grades) === null || _l === void 0 ? void 0 : _l.type) === 'PROGRESS_DEVIATION_LOOKUP')
303
- ? this._renderAssessmentGrades()
304
- : this._isAssessmentOrIntegerScore()
305
- ? this._renderAssessmentGrades()
306
- : html `
281
+ /*
282
+ * scoreType 기반 차트 분기:
283
+ *
284
+ * CUSTOM (X13 등): 2D 룩업 히트맵 — 단, 증강데이터에서는 value가
285
+ * 이미 1~5 정수(룩업 결과)이므로 히트맵 대신 게이지로 폴백.
286
+ * 실제 프로젝트에서는 value가 편차율(소수점)이므로 히트맵 표시.
287
+ *
288
+ * LOOKUP: 성과 분포 커브 (kpi-lookup-chart)
289
+ * ASSESSMENT: 5단계 게이지 (kpi-lookup-chart 자동 감지)
290
+ */ ''}
291
+ ${
292
+ // SL-PA 결과(X34 품질, X43 안전) 만 계단형 등급 분포 차트로 표시.
293
+ // (다른 KPI 차트 경로에는 영향 없음)
294
+ this.selectedKpi.pattern === 'X34' || this.selectedKpi.pattern === 'X43'
295
+ ? html `
296
+ <kpi-step-lookup-chart
297
+ .grades=${this.selectedKpi.grades || []}
298
+ .value=${this.selectedKpi.value}
299
+ .score=${(_l = this.selectedKpi.score) !== null && _l !== void 0 ? _l : null}
300
+ unit=${this.selectedKpi.unit || ''}
301
+ ></kpi-step-lookup-chart>
302
+ `
303
+ : // 2D lookup KPI (X13 등) 는 항상 등급 기준 + 히트맵 함께 표시 (value 가
304
+ // 정수든 소수든 일관). _renderAssessmentGrades 안의 is2D 분기가 히트맵
305
+ // 같이 그림. 그 외 ASSESSMENT 도 등급 기준, LOOKUP 은 분포 커브.
306
+ this.selectedKpi.scoreType === 'CUSTOM' || ((_m = this.selectedKpi.grades) === null || _m === void 0 ? void 0 : _m.type) === 'PROGRESS_DEVIATION_LOOKUP'
307
+ ? this._renderAssessmentGrades()
308
+ : this._isAssessmentOrIntegerScore()
309
+ ? this._renderAssessmentGrades()
310
+ : html `
307
311
  <kpi-lookup-chart
308
312
  .grades=${this.selectedKpi.grades || []}
309
313
  .value=${this.selectedKpi.value}
310
- .score=${(_m = this.selectedKpi.score) !== null && _m !== void 0 ? _m : null}
314
+ .score=${(_o = this.selectedKpi.score) !== null && _o !== void 0 ? _o : null}
311
315
  .scoreType=${this.selectedKpi.scoreType || ''}
312
316
  .valueType=${this.selectedKpi.valueType || ''}
313
317
  unit=${this.selectedKpi.unit || ''}
@@ -520,10 +524,7 @@ let SvProjectDetailPage = class SvProjectDetailPage extends ScopedElementsMixin(
520
524
  const hasValid = (v) => v.value !== null && v.value !== undefined;
521
525
  this.pendingMetrics = targets.filter((m) => {
522
526
  if (m.periodType === 'MONTH') {
523
- return !values.some(v => v.metricId === m.id &&
524
- v.periodType === 'MONTH' &&
525
- (v.valueDate || '').startsWith(lastMonthYm) &&
526
- hasValid(v));
527
+ return !values.some(v => v.metricId === m.id && v.periodType === 'MONTH' && (v.valueDate || '').startsWith(lastMonthYm) && hasValid(v));
527
528
  }
528
529
  return !values.some(v => v.metricId === m.id && v.periodType === m.periodType && hasValid(v));
529
530
  });
@@ -662,6 +663,7 @@ let SvProjectDetailPage = class SvProjectDetailPage extends ScopedElementsMixin(
662
663
  }
663
664
  this.selectedKpi = {
664
665
  kpiName: displayName,
666
+ pattern: kpiNamePattern, // X34/X43 등 — 계단형 차트 라우팅용
665
667
  grades: kpi.grades || [],
666
668
  scoreType: kpi.scoreType || '',
667
669
  valueType: kpi.valueType || '',
@@ -709,8 +711,12 @@ let SvProjectDetailPage = class SvProjectDetailPage extends ScopedElementsMixin(
709
711
  const currentScore = (kpi === null || kpi === void 0 ? void 0 : kpi.score) != null ? Math.round(kpi.score) : 0;
710
712
  const grades = Array.isArray(kpi === null || kpi === void 0 ? void 0 : kpi.grades) ? kpi.grades : [];
711
713
  const colors = ['#e53935', '#ff9800', '#ffca28', '#66bb6a', '#2e7d32'];
712
- const is2D = (kpi === null || kpi === void 0 ? void 0 : kpi.scoreType) === 'CUSTOM' || (kpi === null || kpi === void 0 ? void 0 : kpi.valueType) === 'COMPOSITE' ||
713
- ((kpi === null || kpi === void 0 ? void 0 : kpi.grades) && typeof kpi.grades === 'object' && !Array.isArray(kpi.grades) && kpi.grades.type === 'PROGRESS_DEVIATION_LOOKUP');
714
+ const is2D = (kpi === null || kpi === void 0 ? void 0 : kpi.scoreType) === 'CUSTOM' ||
715
+ (kpi === null || kpi === void 0 ? void 0 : kpi.valueType) === 'COMPOSITE' ||
716
+ ((kpi === null || kpi === void 0 ? void 0 : kpi.grades) &&
717
+ typeof kpi.grades === 'object' &&
718
+ !Array.isArray(kpi.grades) &&
719
+ kpi.grades.type === 'PROGRESS_DEVIATION_LOOKUP');
714
720
  // grades가 있으면 description 사용, 없으면 기본 라벨
715
721
  const defaultLabels = ['매우 미흡', '미흡', '보통', '양호', '우수'];
716
722
  const items = [1, 2, 3, 4, 5].map(score => {
@@ -730,8 +736,7 @@ let SvProjectDetailPage = class SvProjectDetailPage extends ScopedElementsMixin(
730
736
  return html `
731
737
  <div style="padding: 20px; display: flex; flex-direction: column; height: 100%;">
732
738
  ${hasDescriptions
733
- ? items.map(item => html `
734
- <div
739
+ ? items.map(item => html ` <div
735
740
  style="
736
741
  display: flex; align-items: center; gap: 10px;
737
742
  padding: 10px 14px; margin-bottom: 6px;
@@ -749,13 +754,17 @@ let SvProjectDetailPage = class SvProjectDetailPage extends ScopedElementsMixin(
749
754
  display: flex; align-items: center; justify-content: center;
750
755
  font-weight: 700; font-size: 14px; flex-shrink: 0;
751
756
  "
752
- >${item.score}</span>
753
- <span style="font-size: 0.9rem; color: ${item.active ? '#333' : '#666'}; font-weight: ${item.active ? '600' : '400'};">
757
+ >${item.score}</span
758
+ >
759
+ <span
760
+ style="font-size: 0.9rem; color: ${item.active ? '#333' : '#666'}; font-weight: ${item.active
761
+ ? '600'
762
+ : '400'};"
763
+ >
754
764
  ${item.desc}
755
765
  </span>
756
766
  </div>`)
757
767
  : ''}
758
-
759
768
  ${is2D && ((_a = kpi === null || kpi === void 0 ? void 0 : kpi.grades) === null || _a === void 0 ? void 0 : _a.rows)
760
769
  ? html `
761
770
  <div style="flex: 1; display: flex; flex-direction: column; min-height: 0;">
@@ -800,30 +809,30 @@ let SvProjectDetailPage = class SvProjectDetailPage extends ScopedElementsMixin(
800
809
  return {
801
810
  Y1: {
802
811
  name: '일정성과 (Y1)',
803
- score: this.getYKpiScoreFormatted('Y1. 일정성과'),
812
+ score: this.getYKpiScoreFormatted('일정성과(Y1)'),
804
813
  metrics: [
805
814
  { label: '연면적 대비 공사기간', pattern: 'X11', value: this.getMetricValue('X11') },
806
815
  { label: '설계변경 공기 증감률', pattern: 'X12', value: this.getMetricValue('X12') },
807
816
  { label: '일정 이탈 수준', pattern: 'X13', value: this.getMetricValue('X13') },
808
- { label: '일정성과 수준 평가', pattern: 'X14', value: this.getMetricValue('X14') }
817
+ { label: '일정성과 정성 평가', pattern: 'X14', value: this.getMetricValue('X14') }
809
818
  ]
810
819
  },
811
820
  Y2: {
812
821
  name: '비용성과 (Y2)',
813
- score: this.getYKpiScoreFormatted('Y2. 비용성과'),
822
+ score: this.getYKpiScoreFormatted('비용성과(Y2)'),
814
823
  metrics: [
815
824
  { label: '연면적 대비 공사비', pattern: 'X21', value: this.getMetricValue('X21') },
816
- { label: '설계변경 공사비 증감', pattern: 'X22', value: this.getMetricValue('X22') },
817
- { label: '일정성과 수준 평가', pattern: 'X23', value: this.getMetricValue('X23') }
825
+ { label: '설계변경 공사비 증감률', pattern: 'X22', value: this.getMetricValue('X22') },
826
+ { label: '비용성과 정성 평가', pattern: 'X23', value: this.getMetricValue('X23') }
818
827
  ]
819
828
  },
820
829
  Y3: {
821
830
  name: '품질성과 (Y3)',
822
- score: this.getYKpiScoreFormatted('Y3. 품질성과'),
831
+ score: this.getYKpiScoreFormatted('품질성과(Y3)'),
823
832
  metrics: [
824
833
  { label: '검측 불합격률', pattern: 'X33', value: this.getMetricValue('X33') },
825
834
  { label: '품질 SL-PA 결과', pattern: 'X34', value: this.getMetricValue('X34') },
826
- { label: '품질성과 수준 평가', pattern: 'X35', value: this.getMetricValue('X35') }
835
+ { label: '품질성과 정성 평가', pattern: 'X35', value: this.getMetricValue('X35') }
827
836
  ]
828
837
  },
829
838
  Y4: {
@@ -833,41 +842,43 @@ let SvProjectDetailPage = class SvProjectDetailPage extends ScopedElementsMixin(
833
842
  { label: '재해율', pattern: 'X41', value: this.getMetricValue('X41') },
834
843
  { label: '재해강도율', pattern: 'X42', value: this.getMetricValue('X42') },
835
844
  { label: '안전 SL-PA 결과', pattern: 'X43', value: this.getMetricValue('X43') },
836
- { label: '안전성과 수준 평가', pattern: 'X44', value: this.getMetricValue('X44') }
845
+ { label: '안전성과 정성 평가', pattern: 'X44', value: this.getMetricValue('X44') }
837
846
  ]
838
847
  },
839
848
  Y5: {
840
849
  name: '환경성과 (Y5)',
841
- score: this.getYKpiScoreFormatted('Y5. 환경성과'),
850
+ score: this.getYKpiScoreFormatted('환경성과(Y5)'),
842
851
  metrics: [
843
852
  { label: '건설폐기물 발생량', pattern: 'X51', value: this.getMetricValue('X51') },
844
- { label: '환경성과 수준 평가', pattern: 'X52', value: this.getMetricValue('X52') }
853
+ { label: '환경성과 정성 평가', pattern: 'X52', value: this.getMetricValue('X52') }
845
854
  ]
846
855
  },
847
856
  Y6: {
848
857
  name: '생산성 성과 (Y6)',
849
- score: this.getYKpiScoreFormatted('Y6. 생산성성과'),
858
+ score: this.getYKpiScoreFormatted('생산성성과(Y6)'),
850
859
  metrics: [
851
- { label: '투입인력 생산성', pattern: 'X61', value: this.getMetricValue('X61') },
852
- { label: '생산성성과 수준 평가', pattern: 'X62', value: this.getMetricValue('X62') }
860
+ { label: '투입인력(M/M) 생산성', pattern: 'X61', value: this.getMetricValue('X61') },
861
+ { label: '생산성성과 정성 평가', pattern: 'X62', value: this.getMetricValue('X62') }
853
862
  ]
854
863
  }
855
864
  };
856
865
  }
857
866
  getYKpiBoxplotData() {
858
- // KPI 카테고리 매핑 (Y-category KPI names to display categories)
859
- const categoryMapping = {
860
- Y01_productivity: '생산성과',
861
- Y02_schedule: '일정성과',
862
- Y03_cost: '비용성과',
863
- Y04_quality: '품질성과',
864
- Y05_safety: '안전성과',
865
- Y06_environment: '환경성과'
867
+ // kpiName 표기가 환경마다 다양('Y1. 일정성과' / 'Y1. 일정' / '일정성과(Y1)' 등)하므로
868
+ // Y1~Y6 코드만 추출해 짧은 X축 라벨로 매핑 — 전체 문자열에 의존하지 않아 견고함.
869
+ const yLabelByCode = {
870
+ Y1: '일정',
871
+ Y2: '비용',
872
+ Y3: '품질',
873
+ Y4: '안전',
874
+ Y5: '환경',
875
+ Y6: '생산성'
866
876
  };
867
877
  const data = [];
868
878
  // Y-KPI 카테고리별로 통합된 통계 생성
869
879
  this.kpiYComprehensiveStats.forEach(stat => {
870
- const category = categoryMapping[stat.kpiName] || stat.kpiName;
880
+ var _a, _b;
881
+ const category = yLabelByCode[(_b = (_a = (stat.kpiName || '').match(/Y[1-6]/)) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : ''] || stat.kpiName;
871
882
  data.push({
872
883
  org: category, // org 필드 사용 (boxplot 차트가 기대하는 필드명)
873
884
  min: stat.minVal * 100,
@@ -914,20 +925,21 @@ let SvProjectDetailPage = class SvProjectDetailPage extends ScopedElementsMixin(
914
925
  return (kpiValue === null || kpiValue === void 0 ? void 0 : kpiValue.value) || 0;
915
926
  }
916
927
  getRadarChartData() {
917
- // KPI 카테고리 매핑 (Y-category KPI names to display categories)
918
- const categoryMapping = {
919
- Y01_productivity: '생산성',
920
- Y02_schedule: '일정',
921
- Y03_cost: '비용',
922
- Y04_quality: '품질',
923
- Y05_safety: '안전',
924
- Y06_environment: '환경'
928
+ // kpiName 표기가 다양하므로 Y1~Y6 코드만 추출해 짧은 라벨로 매핑 (박스플롯과 동일 방식).
929
+ const yLabelByCode = {
930
+ Y1: '일정',
931
+ Y2: '비용',
932
+ Y3: '품질',
933
+ Y4: '안전',
934
+ Y5: '환경',
935
+ Y6: '생산성'
925
936
  };
926
937
  const data = [];
927
938
  const categories = [];
928
939
  // Create baseline data from comprehensive stats (using avgVal as baseline)
929
940
  this.kpiYComprehensiveStats.forEach(stat => {
930
- const category = categoryMapping[stat.kpiName] || stat.kpiName;
941
+ var _a, _b;
942
+ const category = yLabelByCode[(_b = (_a = (stat.kpiName || '').match(/Y[1-6]/)) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : ''] || stat.kpiName;
931
943
  if (!categories.includes(category)) {
932
944
  categories.push(category);
933
945
  }
@@ -948,7 +960,7 @@ let SvProjectDetailPage = class SvProjectDetailPage extends ScopedElementsMixin(
948
960
  data,
949
961
  categories: categories.length > 0
950
962
  ? categories
951
- : ['Y1. 일정성과', 'Y2. 비용성과', 'Y3. 품질성과', 'Y4. 안전성과', 'Y5. 환경성과', 'Y6. 생산성성과']
963
+ : ['일정', '비용', '품질', '안전', '환경', '생산성']
952
964
  };
953
965
  }
954
966
  getBoxPlotDataForProject(project) {