@dssp/dkpi 1.0.0-alpha.6 → 1.0.0-alpha.60

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 (209) hide show
  1. package/KPI-STATISTICS-SERVICE.md +233 -0
  2. package/assets/favicon.ico +0 -0
  3. package/assets/images/project-image.png +0 -0
  4. package/assets/manifest/apple-1024.png +0 -0
  5. package/assets/manifest/apple-120.png +0 -0
  6. package/assets/manifest/apple-152.png +0 -0
  7. package/assets/manifest/apple-167.png +0 -0
  8. package/assets/manifest/apple-180.png +0 -0
  9. package/assets/manifest/apple-touch-icon.png +0 -0
  10. package/assets/manifest/badge-128x128.png +0 -0
  11. package/assets/manifest/chrome-splashscreen-icon-384x384.png +0 -0
  12. package/assets/manifest/chrome-touch-icon-192x192.png +0 -0
  13. package/assets/manifest/icon-128x128.png +0 -0
  14. package/assets/manifest/icon-192x192.png +0 -0
  15. package/assets/manifest/icon-512x512.png +0 -0
  16. package/assets/manifest/icon-72x72.png +0 -0
  17. package/assets/manifest/icon-96x96.png +0 -0
  18. package/assets/manifest/image-metaog.png +0 -0
  19. package/assets/manifest/maskable_icon.png +0 -0
  20. package/assets/manifest/ms-icon-144x144.png +0 -0
  21. package/assets/manifest/ms-touch-icon-144x144-precomposed.png +0 -0
  22. package/assets/videos/intro.mp4 +0 -0
  23. package/dist-client/bootstrap.js +64 -4
  24. package/dist-client/bootstrap.js.map +1 -1
  25. package/dist-client/components/kpi-boxplot-chart.d.ts +24 -0
  26. package/dist-client/components/kpi-boxplot-chart.js +264 -0
  27. package/dist-client/components/kpi-boxplot-chart.js.map +1 -0
  28. package/dist-client/components/kpi-lookup-chart.d.ts +29 -0
  29. package/dist-client/components/kpi-lookup-chart.js +434 -0
  30. package/dist-client/components/kpi-lookup-chart.js.map +1 -0
  31. package/dist-client/components/kpi-mini-trend-chart.d.ts +14 -0
  32. package/dist-client/components/kpi-mini-trend-chart.js +148 -0
  33. package/dist-client/components/kpi-mini-trend-chart.js.map +1 -0
  34. package/dist-client/components/kpi-radar-chart.d.ts +17 -0
  35. package/dist-client/components/kpi-radar-chart.js +235 -0
  36. package/dist-client/components/kpi-radar-chart.js.map +1 -0
  37. package/dist-client/components/kpi-single-boxplot-chart.d.ts +24 -0
  38. package/dist-client/components/kpi-single-boxplot-chart.js +333 -0
  39. package/dist-client/components/kpi-single-boxplot-chart.js.map +1 -0
  40. package/dist-client/components/kpi-trend-chart.d.ts +25 -0
  41. package/dist-client/components/kpi-trend-chart.js +220 -0
  42. package/dist-client/components/kpi-trend-chart.js.map +1 -0
  43. package/dist-client/components/sv-pagenation-control.d.ts +18 -0
  44. package/dist-client/components/sv-pagenation-control.js +142 -0
  45. package/dist-client/components/sv-pagenation-control.js.map +1 -0
  46. package/dist-client/google-map/common-google-map.d.ts +35 -0
  47. package/dist-client/google-map/common-google-map.js +343 -0
  48. package/dist-client/google-map/common-google-map.js.map +1 -0
  49. package/dist-client/google-map/google-map-loader.d.ts +6 -0
  50. package/dist-client/google-map/google-map-loader.js +23 -0
  51. package/dist-client/google-map/google-map-loader.js.map +1 -0
  52. package/dist-client/icons/menu-icons.d.ts +6 -0
  53. package/dist-client/icons/menu-icons.js +42 -0
  54. package/dist-client/icons/menu-icons.js.map +1 -1
  55. package/dist-client/menu.d.ts +23 -1
  56. package/dist-client/menu.js +57 -2
  57. package/dist-client/menu.js.map +1 -1
  58. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.d.ts +17 -0
  59. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js +280 -0
  60. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js.map +1 -0
  61. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.d.ts +21 -0
  62. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js +389 -0
  63. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js.map +1 -0
  64. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.d.ts +25 -0
  65. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js +469 -0
  66. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js.map +1 -0
  67. package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.d.ts +8 -0
  68. package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.js +78 -0
  69. package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.js.map +1 -0
  70. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.d.ts +34 -0
  71. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js +642 -0
  72. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js.map +1 -0
  73. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.d.ts +38 -0
  74. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js +501 -0
  75. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js.map +1 -0
  76. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.d.ts +26 -0
  77. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js +439 -0
  78. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js.map +1 -0
  79. package/dist-client/pages/kpi-dashboard/kpi-alert-panel.d.ts +18 -0
  80. package/dist-client/pages/kpi-dashboard/kpi-alert-panel.js +131 -0
  81. package/dist-client/pages/kpi-dashboard/kpi-alert-panel.js.map +1 -0
  82. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.d.ts +36 -0
  83. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js +572 -0
  84. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js.map +1 -0
  85. package/dist-client/pages/kpi-dashboard/kpi-dashboard.d.ts +59 -0
  86. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js +1027 -0
  87. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js.map +1 -0
  88. package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.d.ts +12 -0
  89. package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.js +82 -0
  90. package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.js.map +1 -0
  91. package/dist-client/pages/kpi-dashboard/kpi-history-viewer.d.ts +11 -0
  92. package/dist-client/pages/kpi-dashboard/kpi-history-viewer.js +65 -0
  93. package/dist-client/pages/kpi-dashboard/kpi-history-viewer.js.map +1 -0
  94. package/dist-client/pages/kpi-dashboard/kpi-list-summary.d.ts +13 -0
  95. package/dist-client/pages/kpi-dashboard/kpi-list-summary.js +115 -0
  96. package/dist-client/pages/kpi-dashboard/kpi-list-summary.js.map +1 -0
  97. package/dist-client/pages/kpi-dashboard/kpi-performance-summary.d.ts +15 -0
  98. package/dist-client/pages/kpi-dashboard/kpi-performance-summary.js +147 -0
  99. package/dist-client/pages/kpi-dashboard/kpi-performance-summary.js.map +1 -0
  100. package/dist-client/pages/kpi-dashboard/kpi-value-entry.d.ts +7 -0
  101. package/dist-client/pages/kpi-dashboard/kpi-value-entry.js +86 -0
  102. package/dist-client/pages/kpi-dashboard/kpi-value-entry.js.map +1 -0
  103. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.d.ts +58 -0
  104. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js +731 -0
  105. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js.map +1 -0
  106. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.d.ts +23 -0
  107. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js +76 -0
  108. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js.map +1 -0
  109. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.d.ts +69 -0
  110. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js +385 -0
  111. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js.map +1 -0
  112. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.d.ts +12 -0
  113. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js +174 -0
  114. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js.map +1 -0
  115. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.d.ts +41 -0
  116. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js +191 -0
  117. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js.map +1 -0
  118. package/dist-client/pages/kpi-value/kpi-value-importer.d.ts +23 -0
  119. package/dist-client/pages/kpi-value/kpi-value-importer.js +93 -0
  120. package/dist-client/pages/kpi-value/kpi-value-importer.js.map +1 -0
  121. package/dist-client/pages/kpi-value/kpi-value-list-page.d.ts +72 -0
  122. package/dist-client/pages/kpi-value/kpi-value-list-page.js +465 -0
  123. package/dist-client/pages/kpi-value/kpi-value-list-page.js.map +1 -0
  124. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.d.ts +14 -0
  125. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js +315 -0
  126. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js.map +1 -0
  127. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.d.ts +14 -0
  128. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js +275 -0
  129. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js.map +1 -0
  130. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.d.ts +15 -0
  131. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.js +222 -0
  132. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.js.map +1 -0
  133. package/dist-client/pages/sv-project-complete.d.ts +20 -0
  134. package/dist-client/pages/sv-project-complete.js +209 -0
  135. package/dist-client/pages/sv-project-complete.js.map +1 -0
  136. package/dist-client/pages/sv-project-completed-list.d.ts +27 -0
  137. package/dist-client/pages/sv-project-completed-list.js +416 -0
  138. package/dist-client/pages/sv-project-completed-list.js.map +1 -0
  139. package/dist-client/pages/sv-project-detail.d.ts +34 -0
  140. package/dist-client/pages/sv-project-detail.js +1081 -0
  141. package/dist-client/pages/sv-project-detail.js.map +1 -0
  142. package/dist-client/pages/sv-project-list.d.ts +165 -0
  143. package/dist-client/pages/sv-project-list.js +489 -0
  144. package/dist-client/pages/sv-project-list.js.map +1 -0
  145. package/dist-client/route.d.ts +1 -1
  146. package/dist-client/route.js +25 -1
  147. package/dist-client/route.js.map +1 -1
  148. package/dist-client/shared/complete-api.d.ts +8 -0
  149. package/dist-client/shared/complete-api.js +177 -0
  150. package/dist-client/shared/complete-api.js.map +1 -0
  151. package/dist-client/shared/func.d.ts +2 -0
  152. package/dist-client/shared/func.js +22 -0
  153. package/dist-client/shared/func.js.map +1 -0
  154. package/dist-client/themes/dark.css +24 -24
  155. package/dist-client/themes/light.css +23 -23
  156. package/dist-client/tsconfig.tsbuildinfo +1 -1
  157. package/dist-client/viewparts/menu-tools.d.ts +46 -1
  158. package/dist-client/viewparts/menu-tools.js +377 -15
  159. package/dist-client/viewparts/menu-tools.js.map +1 -1
  160. package/dist-server/index.d.ts +2 -0
  161. package/dist-server/index.js +5 -0
  162. package/dist-server/index.js.map +1 -1
  163. package/dist-server/migrations/index.d.ts +1 -0
  164. package/dist-server/migrations/index.js +12 -0
  165. package/dist-server/migrations/index.js.map +1 -0
  166. package/dist-server/scripts/calculate-kpi-scores.d.ts +10 -0
  167. package/dist-server/scripts/calculate-kpi-scores.js +271 -0
  168. package/dist-server/scripts/calculate-kpi-scores.js.map +1 -0
  169. package/dist-server/scripts/load-grade-data-migration.d.ts +10 -0
  170. package/dist-server/scripts/load-grade-data-migration.js +194 -0
  171. package/dist-server/scripts/load-grade-data-migration.js.map +1 -0
  172. package/dist-server/scripts/propagate-parent-kpi-values.d.ts +10 -0
  173. package/dist-server/scripts/propagate-parent-kpi-values.js +440 -0
  174. package/dist-server/scripts/propagate-parent-kpi-values.js.map +1 -0
  175. package/dist-server/service/index.d.ts +4 -0
  176. package/dist-server/service/index.js +20 -0
  177. package/dist-server/service/index.js.map +1 -0
  178. package/dist-server/service/kpi-metric-value/index.d.ts +4 -0
  179. package/dist-server/service/kpi-metric-value/index.js +8 -0
  180. package/dist-server/service/kpi-metric-value/index.js.map +1 -0
  181. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.d.ts +62 -0
  182. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js +572 -0
  183. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js.map +1 -0
  184. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.d.ts +7 -0
  185. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js +47 -0
  186. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js.map +1 -0
  187. package/dist-server/service/kpi-stat/index.d.ts +4 -0
  188. package/dist-server/service/kpi-stat/index.js +8 -0
  189. package/dist-server/service/kpi-stat/index.js.map +1 -0
  190. package/dist-server/service/kpi-stat/kpi-stat-query.d.ts +9 -0
  191. package/dist-server/service/kpi-stat/kpi-stat-query.js +443 -0
  192. package/dist-server/service/kpi-stat/kpi-stat-query.js.map +1 -0
  193. package/dist-server/service/kpi-stat/kpi-stat-types.d.ts +19 -0
  194. package/dist-server/service/kpi-stat/kpi-stat-types.js +130 -0
  195. package/dist-server/service/kpi-stat/kpi-stat-types.js.map +1 -0
  196. package/dist-server/service/kpi-value/index.d.ts +3 -0
  197. package/dist-server/service/kpi-value/index.js +7 -0
  198. package/dist-server/service/kpi-value/index.js.map +1 -0
  199. package/dist-server/service/kpi-value/kpi-value-query.d.ts +8 -0
  200. package/dist-server/service/kpi-value/kpi-value-query.js +69 -0
  201. package/dist-server/service/kpi-value/kpi-value-query.js.map +1 -0
  202. package/dist-server/tsconfig.tsbuildinfo +1 -1
  203. package/kpi-module-service-tests.md +1286 -0
  204. package/kpi-module-test-report.md +676 -0
  205. package/kpi-module-unit-test-detailed-report.md +925 -0
  206. package/kpi-module-unit-tests-detailed.md +1452 -0
  207. package/package.json +62 -51
  208. package/schema.graphql +11559 -3215
  209. package/things-factory.config.js +7 -1
@@ -0,0 +1,1081 @@
1
+ import { __decorate, __metadata } from "tslib";
2
+ import { navigate, PageView } from '@operato/shell';
3
+ import { css, html } from 'lit';
4
+ import { customElement, state } from 'lit/decorators.js';
5
+ import { ScopedElementsMixin } from '@open-wc/scoped-elements';
6
+ import { PROJECT_STATE } from './sv-project-list';
7
+ import { client } from '@operato/graphql';
8
+ import gql from 'graphql-tag';
9
+ import '../components/kpi-boxplot-chart';
10
+ import '../components/kpi-single-boxplot-chart';
11
+ import '../components/kpi-radar-chart';
12
+ import '../components/kpi-lookup-chart';
13
+ let SvProjectDetailPage = class SvProjectDetailPage extends ScopedElementsMixin(PageView) {
14
+ constructor() {
15
+ super(...arguments);
16
+ this.project = {};
17
+ this.kpiComprehensiveStats = [];
18
+ this.kpiYComprehensiveStats = [];
19
+ this.projectXKpiValues = [];
20
+ this.showLookupChart = false;
21
+ this.selectedKpi = null;
22
+ }
23
+ get context() {
24
+ var _a;
25
+ return {
26
+ title: this.project ? `프로젝트 상세 정보 - ${(_a = this.project) === null || _a === void 0 ? void 0 : _a.name}` : '프로젝트 상세 정보'
27
+ };
28
+ }
29
+ render() {
30
+ var _a, _b, _c, _d, _e, _f, _g, _h;
31
+ return html `
32
+ <div content>
33
+ <div left>
34
+ <div class="card">
35
+ <div class="card-title">
36
+ <div>${this.project.name}</div>
37
+ <div class="triangle"></div>
38
+ </div>
39
+
40
+ <div class="top-info" style="margin-top: 10px;">
41
+ <img pic src=${((_a = this.project.mainPhoto) === null || _a === void 0 ? void 0 : _a.fullpath) || '/assets/images/project-image.png'} alt="project" />
42
+ <div class="info">
43
+ <div class="row">
44
+ <div class="label">• 주소</div>
45
+ <div class="value">${(_b = this.project.buildingComplex) === null || _b === void 0 ? void 0 : _b.address}</div>
46
+ </div>
47
+ <div class="row">
48
+ <div class="label">• 공사기간</div>
49
+ <div class="value">${this.project.startDate} ~ ${this.project.endDate}</div>
50
+ </div>
51
+ <div class="row">
52
+ <div class="label">• 현장상태</div>
53
+ <div class="value status-value">${PROJECT_STATE[this.project.state]}</div>
54
+ </div>
55
+ <div class="row">
56
+ <div class="label">• 요청상세</div>
57
+ <div class="value bold">000</div>
58
+ </div>
59
+ <div class="row">
60
+ <div class="label">• 알람</div>
61
+ <div class="value">000</div>
62
+ </div>
63
+ </div>
64
+ </div>
65
+
66
+ <div class="detail-grid">
67
+ <div class="grid-item row">
68
+ <div class="label">• 공사비</div>
69
+ <div class="value">${((_d = (_c = this.project.buildingComplex) === null || _c === void 0 ? void 0 : _c.constructionCost) === null || _d === void 0 ? void 0 : _d.toLocaleString()) || '-'} 원</div>
70
+ </div>
71
+ <div class="grid-item row">
72
+ <div class="label">• 연면적</div>
73
+ <div class="value">${((_f = (_e = this.project.buildingComplex) === null || _e === void 0 ? void 0 : _e.area) === null || _f === void 0 ? void 0 : _f.toLocaleString()) || '-'} ㎡</div>
74
+ </div>
75
+ <div class="grid-item row">
76
+ <div class="label">• 용적률</div>
77
+ <div class="value">000 %</div>
78
+ </div>
79
+ <div class="grid-item row">
80
+ <div class="label">• 투입인력</div>
81
+ <div class="value">000 명</div>
82
+ </div>
83
+ </div>
84
+ </div>
85
+
86
+ <div class="card">
87
+ <div class="card-title">
88
+ <div>입력 대기 중</div>
89
+ <div class="triangle"></div>
90
+ </div>
91
+ <div class="sub-desc">
92
+ 현재 입력 기한이 도래했거나, 입력이 지연되고 있는 KPI 항목입니다. 해당 항목에 대한 데이터를 입력 해 주시기 바랍니다.
93
+ </div>
94
+
95
+ <div class="list-block">
96
+ <div class="list-title">• 입력 대기 중 2건</div>
97
+ <div class="list-text"># 2033312-Y5.1 : 검수자재 불합격률</div>
98
+ <div class="list-text"># 2033312-Y5.1 : 품질시험 불합격 건수</div>
99
+ </div>
100
+ <div class="list-block">
101
+ <div class="list-title">• 입력 기한 초과 1건</div>
102
+ <div class="list-text">
103
+ # 2033312-Y5.2 : 검측 불합격률 <span class="warn">2025.2.12 <md-icon>report</md-icon></span>
104
+ </div>
105
+ </div>
106
+ <div class="list-block">
107
+ <div class="list-title">• 입력 예정 4건</div>
108
+ <div class="list-text"># 2033320-Y4.32 : 연면적 대비 공기입력</div>
109
+ <div class="list-text"># 2012345-Y4.32 : 책임감리원의 일정성과 수준 평가</div>
110
+ <div class="list-text"># 2035641-Y5.22 : 설계 변경에 따른 공기 증감률</div>
111
+ <div class="list-text"># 2096412-Y5.31 : 현장별 일정 이탈 수준</div>
112
+ </div>
113
+ <div class="list-block">
114
+ <div class="list-title">• 입력 기한 없음 3건</div>
115
+ <div class="list-text"># 2033320-Y4.32 : 연면적 대비 공기입력</div>
116
+ <div class="list-text"># 2012345-Y4.32 : 책임감리원의 일정성과 수준 평가</div>
117
+ <div class="list-text"># 2035641-Y5.22 : 설계 변경에 따른 공기 증감률</div>
118
+ </div>
119
+ </div>
120
+ </div>
121
+
122
+ <div right>
123
+ ${html `
124
+ <div class="button-line">
125
+ <div class="ghost-btn" @click=${() => navigate(`project-update/${this.project.id}`)}>
126
+ <md-icon slot="">assignment</md-icon>
127
+ <div>프로젝트 수정</div>
128
+ </div>
129
+ <div class="ghost-btn" @click=${() => navigate(`project-complete/${this.project.id}`)}>
130
+ <md-icon slot="">assignment_turned_in</md-icon>
131
+ <div>프로젝트 완공 처리</div>
132
+ </div>
133
+ </div>
134
+ `}
135
+
136
+ <div class="card kpi-box">
137
+ <div class="kpi-header">
138
+ <div>
139
+ <div>종합 KPI</div>
140
+ <div class="triangle"></div>
141
+ </div>
142
+ <div class="kpi-desc">프로젝트의 6개 성과를 종합한 종합 KPI포인트와 각 지표별 분포</div>
143
+ </div>
144
+
145
+ <div class="kpi-summary">
146
+ <div class="mini-boxplot" title="boxplot placeholder">
147
+ <div class="boxplot-area">
148
+ <kpi-single-boxplot-chart .data=${this.getBoxPlotDataForProject(this.project)}></kpi-single-boxplot-chart>
149
+ </div>
150
+
151
+ <div class="boxplot-point">
152
+ <div class="value">${(_h = (_g = this.project.kpi) === null || _g === void 0 ? void 0 : _g.toFixed(1)) !== null && _h !== void 0 ? _h : '-'}</div>
153
+ <div class="label">Point</div>
154
+ </div>
155
+ </div>
156
+
157
+ <div class="spider-area">
158
+ <sv-kpi-radar-chart
159
+ .data=${this.getRadarChartData().data}
160
+ .categories=${this.getRadarChartData().categories}
161
+ .currentGroup=${'프로젝트성과'}
162
+ ></sv-kpi-radar-chart>
163
+ </div>
164
+ </div>
165
+ </div>
166
+
167
+ <div class="card group-box">
168
+ <div class="kpi-header">
169
+ <div>
170
+ <div>그룹별 분포</div>
171
+ <div class="triangle"></div>
172
+ </div>
173
+ </div>
174
+ <div class="desc">
175
+ Boxplot(박스플롯)은 각 카테고리별로 값의 분포(최소, 1사분위, 중앙값, 3사분위, 최대, 평균, 이상치 등)를 보여줍니다.
176
+ 박스는 중앙 50% 구간, 수염은 전체 범위, 굵은 검정색 가로선은 중앙값(메디안), 초록색 원은 평균값(Mean)을 의미합니다.
177
+ 이 프로젝트의 값은 진한 오렌지색 원으로 별도 강조되어 표시되며, 중앙값/평균과 다를 수 있습니다.
178
+ </div>
179
+ <div class="group-chart">
180
+ <sv-kpi-boxplot-chart .data=${this.getYKpiBoxplotData()}></sv-kpi-boxplot-chart>
181
+ </div>
182
+ </div>
183
+
184
+ <div class="metrics-card">
185
+ <div class="metrics-header">
186
+ <div class="title">
187
+ <span>그룹별 상세 매트릭 데이터</span>
188
+ <div class="triangle"></div>
189
+ </div>
190
+ </div>
191
+ <div class="desc">
192
+ 프로젝트의 6개 성과별 상세 주요 매트릭 데이터 (각 세부 항목을 클릭하시면 평가 기준표를 확인할 수 있습니다)
193
+ </div>
194
+ <div class="metrics-content">
195
+ ${Object.values(this.getMetricGroups()).map(group => html `
196
+ <div class="metric-group">
197
+ <div class="metric-group-header">
198
+ <div class="metric-group-icon"></div>
199
+ <span>${group.name}</span>
200
+ </div>
201
+ <div class="metric-items">
202
+ ${group.metrics.map(metric => html `
203
+ <div class="metric-item">
204
+ <span
205
+ class="metric-label"
206
+ @click=${() => this._onMetricLabelClick(metric)}
207
+ title="클릭하시면 평가 기준표를 확인할 수 있습니다"
208
+ >
209
+ ${metric.label}
210
+ </span>
211
+ <span class="metric-value">${metric.value}</span>
212
+ </div>
213
+ `)}
214
+ </div>
215
+ </div>
216
+ `)}
217
+ </div>
218
+ </div>
219
+ </div>
220
+ </div>
221
+
222
+ ${this.showLookupChart && this.selectedKpi
223
+ ? html `
224
+ <div class="popup-overlay" @click=${this._closePopup}>
225
+ <div class="popup-content" @click=${(e) => e.stopPropagation()}>
226
+ <div class="popup-header">
227
+ <div class="popup-title">${this.selectedKpi.kpiName} - Lookup Table</div>
228
+ <button class="popup-close" @click=${this._closePopup}>×</button>
229
+ </div>
230
+ <div class="popup-chart-container">
231
+ <kpi-lookup-chart
232
+ .grades=${this.selectedKpi.grades || []}
233
+ .value=${this.selectedKpi.value}
234
+ unit=${this.selectedKpi.unit || ''}
235
+ kpiName=${this.selectedKpi.kpiName || ''}
236
+ ></kpi-lookup-chart>
237
+ </div>
238
+ </div>
239
+ </div>
240
+ `
241
+ : ''}
242
+ `;
243
+ }
244
+ async pageUpdated(changes, lifecycle) {
245
+ if (this.active) {
246
+ this.initProject(lifecycle.resourceId);
247
+ this.getKpiComprehensiveStats();
248
+ this.getKpiYComprehensiveStats();
249
+ if (lifecycle.resourceId) {
250
+ this.getProjectXKpiValues(lifecycle.resourceId);
251
+ }
252
+ }
253
+ }
254
+ async initProject(projectId = '') {
255
+ var _a;
256
+ const response = await client.query({
257
+ query: gql `
258
+ query Project($id: String!) {
259
+ project(id: $id) {
260
+ id
261
+ name
262
+ state
263
+ startDate
264
+ endDate
265
+ projectType
266
+ mainPhoto {
267
+ fullpath
268
+ }
269
+ totalProgress
270
+ weeklyProgress
271
+ kpi
272
+ kpiValues {
273
+ kpiName
274
+ value
275
+ }
276
+ inspPassRate
277
+ robotProgressRate
278
+ structuralSafetyRate
279
+ robotCount
280
+ buildingComplex {
281
+ id
282
+ address
283
+ latitude
284
+ longitude
285
+ area
286
+ clientCompany
287
+ constructionCompany
288
+ supervisoryCompany
289
+ designCompany
290
+ drawing {
291
+ id
292
+ name
293
+ fullpath
294
+ }
295
+ constructionType
296
+ constructionCost
297
+ etc
298
+ notice
299
+ householdCount
300
+ buildingCount
301
+ virtualTourLink
302
+ buildings {
303
+ id
304
+ name
305
+ floorCount
306
+ }
307
+ }
308
+ }
309
+ }
310
+ `,
311
+ variables: { id: projectId }
312
+ });
313
+ if (response.errors)
314
+ return;
315
+ this.project = ((_a = response.data) === null || _a === void 0 ? void 0 : _a.project) || {};
316
+ this.updateContext();
317
+ }
318
+ async getKpiComprehensiveStats() {
319
+ try {
320
+ const response = await client.query({
321
+ query: gql `
322
+ query GetKpiZValueComprehensiveStats {
323
+ totalKpiZValueComprehensiveStats {
324
+ minVal
325
+ q1Val
326
+ medVal
327
+ q3Val
328
+ maxVal
329
+ }
330
+ }
331
+ `
332
+ });
333
+ this.kpiComprehensiveStats = response.data.totalKpiZValueComprehensiveStats || [];
334
+ console.log('KPI Z-Value Comprehensive Stats:', this.kpiComprehensiveStats);
335
+ }
336
+ catch (error) {
337
+ console.error('Failed to fetch KPI comprehensive stats:', error);
338
+ this.kpiComprehensiveStats = [];
339
+ }
340
+ }
341
+ async getKpiYComprehensiveStats() {
342
+ try {
343
+ const response = await client.query({
344
+ query: gql `
345
+ query GetKpiYValueComprehensiveStats {
346
+ totalKpiYValueComprehensiveStats {
347
+ kpiName
348
+ minVal
349
+ q1Val
350
+ medVal
351
+ q3Val
352
+ maxVal
353
+ avgVal
354
+ projectCount
355
+ }
356
+ }
357
+ `
358
+ });
359
+ this.kpiYComprehensiveStats = response.data.totalKpiYValueComprehensiveStats || [];
360
+ console.log('KPI Y-Value Comprehensive Stats:', this.kpiYComprehensiveStats);
361
+ }
362
+ catch (error) {
363
+ console.error('Failed to fetch KPI Y comprehensive stats:', error);
364
+ this.kpiYComprehensiveStats = [];
365
+ }
366
+ }
367
+ async getProjectXKpiValues(projectId) {
368
+ try {
369
+ const response = await client.query({
370
+ query: gql `
371
+ query GetProjectXKpiValues($projectId: String!) {
372
+ projectXKpiValues(projectId: $projectId) {
373
+ id
374
+ value
375
+ valueDate
376
+ kpi {
377
+ id
378
+ name
379
+ description
380
+ }
381
+ }
382
+ }
383
+ `,
384
+ variables: { projectId }
385
+ });
386
+ this.projectXKpiValues = response.data.projectXKpiValues || [];
387
+ console.log('Project X KPI Values:', this.projectXKpiValues);
388
+ }
389
+ catch (error) {
390
+ console.error('Failed to fetch project X KPI values:', error);
391
+ this.projectXKpiValues = [];
392
+ }
393
+ }
394
+ _onMetricLabelClick(metric) {
395
+ // metric.pattern을 사용하여 X-KPI 조회
396
+ if (!metric.pattern) {
397
+ console.warn('No pattern found for metric:', metric);
398
+ return;
399
+ }
400
+ // X-KPI 이름 패턴 (예: X11, X12, X21, ...)
401
+ const kpiNamePattern = metric.pattern;
402
+ // 해당 KPI 정보 조회
403
+ this._fetchKpiWithGradesByPattern(kpiNamePattern, metric.label);
404
+ }
405
+ async _fetchKpiWithGradesByPattern(kpiNamePattern, displayName) {
406
+ var _a, _b;
407
+ try {
408
+ // X-KPI 이름 패턴으로 전체 이름 찾기 (projectXKpiValues에서)
409
+ const kpiValue = this.projectXKpiValues.find(kv => { var _a; return ((_a = kv.kpi) === null || _a === void 0 ? void 0 : _a.name) && kv.kpi.name.includes(kpiNamePattern); });
410
+ if (!kpiValue || !kpiValue.kpi) {
411
+ console.warn(`No KPI found for pattern: ${kpiNamePattern}`);
412
+ return;
413
+ }
414
+ // GraphQL로 KPI 상세 정보 (grades 포함) 조회
415
+ const response = await client.query({
416
+ query: gql `
417
+ query Kpi($id: String!) {
418
+ kpi(id: $id) {
419
+ id
420
+ name
421
+ grades
422
+ vizMeta
423
+ }
424
+ }
425
+ `,
426
+ variables: { id: kpiValue.kpi.id }
427
+ });
428
+ if (response.errors || !((_a = response.data) === null || _a === void 0 ? void 0 : _a.kpi)) {
429
+ console.error('KPI not found or error:', response.errors);
430
+ return;
431
+ }
432
+ const kpi = response.data.kpi;
433
+ // grades가 없으면 경고
434
+ if (!kpi.grades || kpi.grades.length === 0) {
435
+ console.warn(`No grades found for KPI: ${kpiValue.kpi.name}`);
436
+ // 그래도 팝업은 띄워서 "No grade data available" 메시지 표시
437
+ }
438
+ this.selectedKpi = {
439
+ kpiName: displayName,
440
+ grades: kpi.grades || [],
441
+ value: kpiValue.value || null,
442
+ unit: ((_b = kpi.vizMeta) === null || _b === void 0 ? void 0 : _b.unit) || ''
443
+ };
444
+ this.showLookupChart = true;
445
+ }
446
+ catch (error) {
447
+ console.error('Error fetching KPI:', error);
448
+ }
449
+ }
450
+ _closePopup() {
451
+ this.showLookupChart = false;
452
+ this.selectedKpi = null;
453
+ }
454
+ getMetricValue(kpiNamePattern) {
455
+ const kpiValue = this.projectXKpiValues.find(kv => { var _a; return ((_a = kv.kpi) === null || _a === void 0 ? void 0 : _a.name) && kv.kpi.name.includes(kpiNamePattern); });
456
+ const value = (kpiValue === null || kpiValue === void 0 ? void 0 : kpiValue.value) || 0;
457
+ // 정수면 그대로, 소수면 최대 3자리까지만 표시
458
+ if (Number.isInteger(value)) {
459
+ return value.toString();
460
+ }
461
+ else {
462
+ return value.toFixed(3).replace(/\.?0+$/, '');
463
+ }
464
+ }
465
+ getMetricGroups() {
466
+ return {
467
+ Y1: {
468
+ name: '일정성과 (Y1)',
469
+ metrics: [
470
+ { label: '연면적 대비 공사기간', pattern: 'X11', value: this.getMetricValue('X11') },
471
+ { label: '설계변경 공기 증감률', pattern: 'X12', value: this.getMetricValue('X12') },
472
+ { label: '일정 이탈 수준', pattern: 'X13', value: this.getMetricValue('X13') },
473
+ { label: '일정성과 수준 평가', pattern: 'X14', value: this.getMetricValue('X14') }
474
+ ]
475
+ },
476
+ Y2: {
477
+ name: '비용성과 (Y2)',
478
+ metrics: [
479
+ { label: '연면적 대비 공사비', pattern: 'X21', value: this.getMetricValue('X21') },
480
+ { label: '설계변경 공사비 증감', pattern: 'X22', value: this.getMetricValue('X22') },
481
+ { label: '일정성과 수준 평가', pattern: 'X23', value: this.getMetricValue('X23') }
482
+ ]
483
+ },
484
+ Y3: {
485
+ name: '품질성과 (Y3)',
486
+ metrics: [
487
+ { label: '품질시험 불합격률', pattern: 'X31', value: this.getMetricValue('X31') },
488
+ { label: '검수자재 불합격률', pattern: 'X32', value: this.getMetricValue('X32') },
489
+ { label: '검측 불합격률', pattern: 'X33', value: this.getMetricValue('X33') },
490
+ { label: '품질 SL-PA 결과', pattern: 'X34', value: this.getMetricValue('X34') },
491
+ { label: '품질성과 수준 평가', pattern: 'X35', value: this.getMetricValue('X35') }
492
+ ]
493
+ },
494
+ Y4: {
495
+ name: '안전성과 (Y4)',
496
+ metrics: [
497
+ { label: '재해율', pattern: 'X41', value: this.getMetricValue('X41') },
498
+ { label: '재해강도율', pattern: 'X42', value: this.getMetricValue('X42') },
499
+ { label: '안전 SL-PA 결과', pattern: 'X43', value: this.getMetricValue('X43') },
500
+ { label: '안전성과 수준 평가', pattern: 'X44', value: this.getMetricValue('X44') }
501
+ ]
502
+ },
503
+ Y5: {
504
+ name: '환경성과 (Y5)',
505
+ metrics: [
506
+ { label: '건설폐기물 발생량', pattern: 'X51', value: this.getMetricValue('X51') },
507
+ { label: '환경성과 수준 평가', pattern: 'X52', value: this.getMetricValue('X52') }
508
+ ]
509
+ },
510
+ Y6: {
511
+ name: '생산성 성과 (Y6)',
512
+ metrics: [
513
+ { label: '투입인력 생산성', pattern: 'X61', value: this.getMetricValue('X61') },
514
+ { label: '생산성성과 수준 평가', pattern: 'X62', value: this.getMetricValue('X62') }
515
+ ]
516
+ }
517
+ };
518
+ }
519
+ getYKpiBoxplotData() {
520
+ // KPI 카테고리 매핑 (Y-category KPI names to display categories)
521
+ const categoryMapping = {
522
+ Y01_productivity: '생산성과',
523
+ Y02_schedule: '일정성과',
524
+ Y03_cost: '비용성과',
525
+ Y04_quality: '품질성과',
526
+ Y05_safety: '안전성과',
527
+ Y06_environment: '환경성과'
528
+ };
529
+ const data = [];
530
+ // Y-KPI 카테고리별로 통합된 통계 생성
531
+ this.kpiYComprehensiveStats.forEach(stat => {
532
+ const category = categoryMapping[stat.kpiName] || stat.kpiName;
533
+ data.push({
534
+ org: category, // org 필드 사용 (boxplot 차트가 기대하는 필드명)
535
+ min: stat.minVal * 20,
536
+ max: stat.maxVal * 20,
537
+ q1: stat.q1Val * 20,
538
+ q3: stat.q3Val * 20,
539
+ median: stat.medVal * 20,
540
+ mean: stat.avgVal * 20,
541
+ value: this.getProjectYKpiValue(stat.kpiName) // 현재 프로젝트의 실제 Y-KPI 값
542
+ });
543
+ });
544
+ return data.sort((a, b) => a.org.localeCompare(b.org));
545
+ }
546
+ getProjectYKpiValue(kpiName) {
547
+ var _a, _b;
548
+ // 현재 프로젝트의 kpiValues 배열에서 해당 KPI 값을 찾습니다
549
+ const kpiValue = (_b = (_a = this.project) === null || _a === void 0 ? void 0 : _a.kpiValues) === null || _b === void 0 ? void 0 : _b.find(kv => kv.kpiName === kpiName);
550
+ return (kpiValue === null || kpiValue === void 0 ? void 0 : kpiValue.value) || 0;
551
+ }
552
+ getRadarChartData() {
553
+ // KPI 카테고리 매핑 (Y-category KPI names to display categories)
554
+ const categoryMapping = {
555
+ Y01_productivity: '생산성',
556
+ Y02_schedule: '일정',
557
+ Y03_cost: '비용',
558
+ Y04_quality: '품질',
559
+ Y05_safety: '안전',
560
+ Y06_environment: '환경'
561
+ };
562
+ const data = [];
563
+ const categories = [];
564
+ // Create baseline data from comprehensive stats (using avgVal as baseline)
565
+ this.kpiYComprehensiveStats.forEach(stat => {
566
+ const category = categoryMapping[stat.kpiName] || stat.kpiName;
567
+ if (!categories.includes(category)) {
568
+ categories.push(category);
569
+ }
570
+ // Add baseline average
571
+ data.push({
572
+ org: '기준평균',
573
+ category,
574
+ value: Math.round(stat.avgVal * 20) // Scale to match expected range
575
+ });
576
+ // Add current project performance (using project's individual values)
577
+ // For now, using avgVal + some variation as placeholder
578
+ data.push({
579
+ org: '프로젝트성과',
580
+ category,
581
+ value: this.getProjectYKpiValue(stat.kpiName)
582
+ });
583
+ });
584
+ return {
585
+ data,
586
+ categories: categories.length > 0
587
+ ? categories
588
+ : ['Y1. 일정성과', 'Y2. 비용성과', 'Y3. 품질성과', 'Y4. 안전성과', 'Y5. 환경성과', 'Y6. 생산성성과']
589
+ };
590
+ }
591
+ getBoxPlotDataForProject(project) {
592
+ var _a;
593
+ // 프로젝트의 지역(geo_group)에 해당하는 통계 찾기
594
+ const stats = (_a = this.kpiComprehensiveStats) === null || _a === void 0 ? void 0 : _a[0];
595
+ if (!stats) {
596
+ // 전체 통계의 평균값 사용 또는 기본값 반환
597
+ const defaultStats = this.kpiComprehensiveStats.length > 0 ? this.kpiComprehensiveStats[0] : null;
598
+ if (defaultStats) {
599
+ return {
600
+ min: defaultStats.minVal,
601
+ max: defaultStats.maxVal,
602
+ mean: (defaultStats.q1Val + defaultStats.q3Val) / 2,
603
+ median: defaultStats.medVal,
604
+ q1: defaultStats.q1Val,
605
+ q3: defaultStats.q3Val,
606
+ value: project.kpi || 0
607
+ };
608
+ }
609
+ // 완전한 기본값
610
+ return {
611
+ min: 0,
612
+ max: 100,
613
+ mean: 50,
614
+ median: 50,
615
+ q1: 25,
616
+ q3: 75,
617
+ value: project.kpi || 0
618
+ };
619
+ }
620
+ return {
621
+ min: stats.minVal * 20,
622
+ max: stats.maxVal * 20,
623
+ mean: ((stats.q1Val + stats.q3Val) / 2) * 20, // 대략적 평균값
624
+ median: stats.medVal * 20,
625
+ q1: stats.q1Val * 20,
626
+ q3: stats.q3Val * 20,
627
+ value: project.kpi || 0
628
+ };
629
+ }
630
+ };
631
+ SvProjectDetailPage.styles = [
632
+ css `
633
+ :host {
634
+ display: flex;
635
+ flex-direction: column;
636
+ overflow-y: auto;
637
+
638
+ width: 100%;
639
+ height: 100%;
640
+ background-color: var(--md-sys-color-background, #f6f6f6);
641
+ }
642
+
643
+ /* content layout */
644
+ div[content] {
645
+ display: flex;
646
+ gap: 35px;
647
+ padding: 25px;
648
+ }
649
+ div[left] {
650
+ flex: 1;
651
+ display: flex;
652
+ flex-direction: column;
653
+ gap: 20px;
654
+ }
655
+ div[right] {
656
+ flex: 1;
657
+ display: flex;
658
+ flex-direction: column;
659
+ gap: 20px;
660
+ padding: 0 1px;
661
+ }
662
+
663
+ /* card */
664
+ .card {
665
+ background: #ffffff;
666
+ border-radius: 10px;
667
+ padding: 15px;
668
+ box-shadow: 3px 3px 3px 0 rgba(0, 0, 0, 0.1);
669
+ }
670
+ .card-title {
671
+ display: flex;
672
+ align-items: center;
673
+ gap: 5px;
674
+ color: #35618e;
675
+ font-weight: 700;
676
+ }
677
+ .triangle {
678
+ width: 0;
679
+ height: 0;
680
+ border-left: 6px solid transparent;
681
+ border-right: 6px solid transparent;
682
+ border-top: 8px solid #35618e;
683
+ }
684
+
685
+ /* left top info box */
686
+ .bold {
687
+ font-weight: bold;
688
+ }
689
+ .top-info {
690
+ display: flex;
691
+ gap: 10px;
692
+ }
693
+ .top-info img[pic] {
694
+ width: 284px;
695
+ height: 160px;
696
+ object-fit: fill;
697
+ background: #e5e7eb;
698
+ }
699
+ .top-info .info {
700
+ display: flex;
701
+ flex-direction: column;
702
+ gap: 15px;
703
+ padding: 10px 0;
704
+ flex: 1;
705
+ }
706
+ .row {
707
+ display: flex;
708
+ align-items: center;
709
+ gap: 10px;
710
+ }
711
+ .label {
712
+ min-width: fit-content;
713
+ color: #35618e;
714
+ font-size: 16px;
715
+ letter-spacing: -0.05em;
716
+ }
717
+ .value {
718
+ color: #333;
719
+ font-size: 16px;
720
+ }
721
+ .status-value {
722
+ color: #4cbb49;
723
+ font-weight: 700;
724
+ }
725
+
726
+ .address {
727
+ color: #35618e;
728
+ font-size: 16px;
729
+ letter-spacing: -0.05em;
730
+ }
731
+
732
+ .detail-grid {
733
+ display: grid;
734
+ grid-template-columns: 284px 1fr;
735
+ gap: 15px 10px;
736
+ padding: 5px 0;
737
+ }
738
+ .detail-grid .grid-item .label {
739
+ width: auto;
740
+ }
741
+
742
+ /* left second card (requests) */
743
+ .sub-desc {
744
+ color: #35618e;
745
+ font-size: 14px;
746
+ letter-spacing: -0.05em;
747
+ margin-bottom: 8px;
748
+ }
749
+ .list-block {
750
+ display: flex;
751
+ flex-direction: column;
752
+ gap: 5px;
753
+ padding: 5px 25px;
754
+ }
755
+ .list-title {
756
+ color: #212529;
757
+ font-weight: 700;
758
+ font-size: 16px;
759
+ letter-spacing: -0.05em;
760
+ }
761
+ .list-text {
762
+ color: #35618e;
763
+ font-size: 16px;
764
+ letter-spacing: -0.05em;
765
+ margin-left: 8px;
766
+ }
767
+ .list-text .warn {
768
+ margin-left: 10px;
769
+ color: #e13232;
770
+
771
+ md-icon {
772
+ font-size: 17px;
773
+ width: 13px;
774
+ height: 13px;
775
+ }
776
+ }
777
+
778
+ /* right buttons */
779
+ .button-line {
780
+ display: flex;
781
+ justify-content: flex-end;
782
+ gap: 10px;
783
+ }
784
+ .ghost-btn {
785
+ display: flex;
786
+ align-items: center;
787
+ gap: 3px;
788
+ padding: 5px 10px;
789
+ background: #35618e;
790
+ color: #ffffff;
791
+ border-radius: 5px;
792
+ box-shadow: 2px 2px 2px 0 rgba(0, 0, 0, 0.1);
793
+ cursor: pointer;
794
+
795
+ /* Material Symbols Outlined (stroke style) */
796
+ md-icon {
797
+ font-variation-settings:
798
+ 'FILL' 0,
799
+ 'wght' 300,
800
+ 'GRAD' 0,
801
+ 'opsz' 24;
802
+ }
803
+ }
804
+
805
+ /* right KPI box */
806
+ .kpi-box {
807
+ display: flex;
808
+ flex-direction: column;
809
+ gap: 10px;
810
+ padding: 15px;
811
+ max-height: 420px;
812
+ min-width: 560px;
813
+ }
814
+ .kpi-header {
815
+ display: flex;
816
+ align-items: center;
817
+ justify-content: space-between;
818
+ color: #35618e;
819
+ font-weight: 700;
820
+ font-size: 20px;
821
+ letter-spacing: -0.05em;
822
+ }
823
+ .kpi-header div {
824
+ display: flex;
825
+ align-items: center;
826
+ gap: 5px;
827
+ }
828
+ .kpi-desc {
829
+ color: #35618e;
830
+ font-size: 13px;
831
+ letter-spacing: -0.05em;
832
+ }
833
+ .kpi-summary {
834
+ display: flex;
835
+ align-items: center;
836
+ padding: 0 30px;
837
+ max-height: 360px;
838
+ gap: 20px;
839
+ }
840
+ .mini-boxplot {
841
+ flex: 1;
842
+ display: flex;
843
+ flex-direction: column;
844
+ height: 100%;
845
+ position: relative;
846
+ min-width: 120px;
847
+ }
848
+ .boxplot-area {
849
+ flex: 1;
850
+ width: 100%;
851
+ display: flex;
852
+ flex-direction: column;
853
+ align-items: center;
854
+ background: #ffffff;
855
+ }
856
+
857
+ .boxplot-point {
858
+ display: flex;
859
+ flex-direction: column;
860
+ align-items: center;
861
+ color: #35618e;
862
+ }
863
+ .boxplot-point .value {
864
+ font-size: 42px;
865
+ font-weight: 800;
866
+ color: #35618e;
867
+ border-radius: 6px;
868
+ }
869
+ .boxplot-point .label {
870
+ font-size: 14px;
871
+ font-weight: 300;
872
+ width: auto;
873
+ }
874
+
875
+ /* spider chart placeholder */
876
+ .spider-area {
877
+ flex: 5;
878
+ display: flex;
879
+ flex-direction: column;
880
+ align-items: center;
881
+ justify-content: center;
882
+ gap: 10px;
883
+ height: 100%;
884
+ }
885
+
886
+ /* group distribution */
887
+ .group-box .desc {
888
+ color: #35618e;
889
+ font-size: 13px;
890
+ letter-spacing: -0.05em;
891
+ margin-top: 4px;
892
+ }
893
+ .group-chart {
894
+ height: 300px;
895
+ margin-top: 25px;
896
+ }
897
+
898
+ kpi-single-boxplot-chart {
899
+ flex: 1;
900
+ }
901
+
902
+ /* KPI 메트릭 데이터 카드 */
903
+ .metrics-card {
904
+ background: #ffffff;
905
+ border-radius: 10px;
906
+ padding: 20px;
907
+ box-shadow: 3px 3px 3px 0 rgba(0, 0, 0, 0.1);
908
+ max-width: 100%;
909
+ overflow: hidden;
910
+ }
911
+ .metrics-header {
912
+ display: flex;
913
+ align-items: center;
914
+ justify-content: space-between;
915
+ }
916
+ .metrics-header .title {
917
+ display: flex;
918
+ align-items: center;
919
+ gap: 8px;
920
+ color: #35618e;
921
+ font-weight: 700;
922
+ font-size: 18px;
923
+ }
924
+ .metrics-card .desc {
925
+ color: #35618e;
926
+ font-size: 13px;
927
+ letter-spacing: -0.05em;
928
+ margin-top: 4px;
929
+ margin-bottom: 15px;
930
+ }
931
+ .metrics-content {
932
+ display: flex;
933
+ flex-direction: column;
934
+ gap: 12px;
935
+ }
936
+ .metric-group {
937
+ border-bottom: 1px dotted #ddd;
938
+ padding-bottom: 12px;
939
+ }
940
+ .metric-group:last-child {
941
+ border-bottom: none;
942
+ padding-bottom: 0;
943
+ }
944
+ .metric-group-header {
945
+ display: flex;
946
+ align-items: center;
947
+ gap: 8px;
948
+ margin-bottom: 8px;
949
+ color: #35618e;
950
+ font-weight: 600;
951
+ font-size: 16px;
952
+ }
953
+ .metric-group-icon {
954
+ width: 20px;
955
+ height: 20px;
956
+ background: #35618e;
957
+ border-radius: 3px;
958
+ position: relative;
959
+ }
960
+ .metric-group-icon::before {
961
+ content: '✓';
962
+ position: absolute;
963
+ top: 50%;
964
+ left: 50%;
965
+ transform: translate(-50%, -50%);
966
+ color: white;
967
+ font-size: 12px;
968
+ font-weight: bold;
969
+ }
970
+ .metric-items {
971
+ display: grid;
972
+ grid-template-columns: 1fr 1fr 1fr;
973
+ gap: 8px 16px;
974
+ padding-left: 28px;
975
+ }
976
+ .metric-item {
977
+ display: flex;
978
+ justify-content: space-between;
979
+ align-items: center;
980
+ font-size: 14px;
981
+ }
982
+ .metric-label {
983
+ color: #333;
984
+ flex: 1;
985
+ }
986
+ .metric-value {
987
+ color: #35618e;
988
+ font-weight: 600;
989
+ min-width: 40px;
990
+ text-align: right;
991
+ }
992
+
993
+ /* Popup overlay */
994
+ .popup-overlay {
995
+ position: fixed;
996
+ top: 0;
997
+ left: 0;
998
+ right: 0;
999
+ bottom: 0;
1000
+ background: rgba(0, 0, 0, 0.5);
1001
+ display: flex;
1002
+ align-items: center;
1003
+ justify-content: center;
1004
+ z-index: 1000;
1005
+ }
1006
+ .popup-content {
1007
+ background: white;
1008
+ border-radius: 12px;
1009
+ padding: 24px;
1010
+ max-width: 800px;
1011
+ width: 90%;
1012
+ max-height: 80vh;
1013
+ overflow-y: auto;
1014
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
1015
+ }
1016
+ .popup-header {
1017
+ display: flex;
1018
+ justify-content: space-between;
1019
+ align-items: center;
1020
+ margin-bottom: 20px;
1021
+ padding-bottom: 16px;
1022
+ border-bottom: 2px solid #e0e0e0;
1023
+ }
1024
+ .popup-title {
1025
+ font-size: 20px;
1026
+ font-weight: 700;
1027
+ color: #35618e;
1028
+ }
1029
+ .popup-close {
1030
+ cursor: pointer;
1031
+ font-size: 24px;
1032
+ color: #999;
1033
+ background: none;
1034
+ border: none;
1035
+ padding: 4px 8px;
1036
+ }
1037
+ .popup-close:hover {
1038
+ color: #333;
1039
+ }
1040
+ .popup-chart-container {
1041
+ min-height: 400px;
1042
+ height: 500px;
1043
+ }
1044
+ .metric-label {
1045
+ cursor: pointer;
1046
+ }
1047
+ .metric-label:hover {
1048
+ opacity: 0.7;
1049
+ text-decoration: underline;
1050
+ }
1051
+ `
1052
+ ];
1053
+ __decorate([
1054
+ state(),
1055
+ __metadata("design:type", Object)
1056
+ ], SvProjectDetailPage.prototype, "project", void 0);
1057
+ __decorate([
1058
+ state(),
1059
+ __metadata("design:type", Array)
1060
+ ], SvProjectDetailPage.prototype, "kpiComprehensiveStats", void 0);
1061
+ __decorate([
1062
+ state(),
1063
+ __metadata("design:type", Array)
1064
+ ], SvProjectDetailPage.prototype, "kpiYComprehensiveStats", void 0);
1065
+ __decorate([
1066
+ state(),
1067
+ __metadata("design:type", Array)
1068
+ ], SvProjectDetailPage.prototype, "projectXKpiValues", void 0);
1069
+ __decorate([
1070
+ state(),
1071
+ __metadata("design:type", Boolean)
1072
+ ], SvProjectDetailPage.prototype, "showLookupChart", void 0);
1073
+ __decorate([
1074
+ state(),
1075
+ __metadata("design:type", Object)
1076
+ ], SvProjectDetailPage.prototype, "selectedKpi", void 0);
1077
+ SvProjectDetailPage = __decorate([
1078
+ customElement('sv-project-detail')
1079
+ ], SvProjectDetailPage);
1080
+ export { SvProjectDetailPage };
1081
+ //# sourceMappingURL=sv-project-detail.js.map