@dssp/dkpi 1.0.0-alpha.9 → 1.0.0-alpha.91

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 (261) hide show
  1. package/KPI-STATISTICS-SERVICE.md +233 -0
  2. package/_index.html +4 -9
  3. package/assets/favicon.ico +0 -0
  4. package/assets/helps/index.md +1 -1
  5. package/assets/images/project-image.png +0 -0
  6. package/assets/manifest/apple-1024.png +0 -0
  7. package/assets/manifest/apple-120.png +0 -0
  8. package/assets/manifest/apple-152.png +0 -0
  9. package/assets/manifest/apple-167.png +0 -0
  10. package/assets/manifest/apple-180.png +0 -0
  11. package/assets/manifest/apple-touch-icon.png +0 -0
  12. package/assets/manifest/badge-128x128.png +0 -0
  13. package/assets/manifest/chrome-splashscreen-icon-384x384.png +0 -0
  14. package/assets/manifest/chrome-touch-icon-192x192.png +0 -0
  15. package/assets/manifest/icon-128x128.png +0 -0
  16. package/assets/manifest/icon-192x192.png +0 -0
  17. package/assets/manifest/icon-512x512.png +0 -0
  18. package/assets/manifest/icon-72x72.png +0 -0
  19. package/assets/manifest/icon-96x96.png +0 -0
  20. package/assets/manifest/image-metaog.png +0 -0
  21. package/assets/manifest/maskable_icon.png +0 -0
  22. package/assets/manifest/ms-icon-144x144.png +0 -0
  23. package/assets/manifest/ms-touch-icon-144x144-precomposed.png +0 -0
  24. package/assets/manifest.json +2 -2
  25. package/assets/videos/intro.mp4 +0 -0
  26. package/config/config.development.js +2 -1
  27. package/config/config.production.js +2 -1
  28. package/dist-client/bootstrap.d.ts +1 -0
  29. package/dist-client/bootstrap.js +75 -4
  30. package/dist-client/bootstrap.js.map +1 -1
  31. package/dist-client/components/kpi-2d-lookup-chart.d.ts +50 -0
  32. package/dist-client/components/kpi-2d-lookup-chart.js +263 -0
  33. package/dist-client/components/kpi-2d-lookup-chart.js.map +1 -0
  34. package/dist-client/components/kpi-boxplot-chart.d.ts +31 -0
  35. package/dist-client/components/kpi-boxplot-chart.js +306 -0
  36. package/dist-client/components/kpi-boxplot-chart.js.map +1 -0
  37. package/dist-client/components/kpi-lookup-chart.d.ts +58 -0
  38. package/dist-client/components/kpi-lookup-chart.js +459 -0
  39. package/dist-client/components/kpi-lookup-chart.js.map +1 -0
  40. package/dist-client/components/kpi-mini-trend-chart.d.ts +14 -0
  41. package/dist-client/components/kpi-mini-trend-chart.js +180 -0
  42. package/dist-client/components/kpi-mini-trend-chart.js.map +1 -0
  43. package/dist-client/components/kpi-radar-chart.d.ts +17 -0
  44. package/dist-client/components/kpi-radar-chart.js +259 -0
  45. package/dist-client/components/kpi-radar-chart.js.map +1 -0
  46. package/dist-client/components/kpi-single-boxplot-chart.d.ts +36 -0
  47. package/dist-client/components/kpi-single-boxplot-chart.js +417 -0
  48. package/dist-client/components/kpi-single-boxplot-chart.js.map +1 -0
  49. package/dist-client/components/kpi-step-lookup-chart.d.ts +33 -0
  50. package/dist-client/components/kpi-step-lookup-chart.js +181 -0
  51. package/dist-client/components/kpi-step-lookup-chart.js.map +1 -0
  52. package/dist-client/components/kpi-trend-chart.d.ts +25 -0
  53. package/dist-client/components/kpi-trend-chart.js +241 -0
  54. package/dist-client/components/kpi-trend-chart.js.map +1 -0
  55. package/dist-client/components/sv-pagenation-control.d.ts +18 -0
  56. package/dist-client/components/sv-pagenation-control.js +142 -0
  57. package/dist-client/components/sv-pagenation-control.js.map +1 -0
  58. package/dist-client/entries/auth/checkin.d.ts +38 -0
  59. package/dist-client/entries/auth/checkin.js +551 -0
  60. package/dist-client/entries/auth/checkin.js.map +1 -0
  61. package/dist-client/google-map/common-google-map.d.ts +35 -0
  62. package/dist-client/google-map/common-google-map.js +349 -0
  63. package/dist-client/google-map/common-google-map.js.map +1 -0
  64. package/dist-client/google-map/google-map-loader.d.ts +6 -0
  65. package/dist-client/google-map/google-map-loader.js +23 -0
  66. package/dist-client/google-map/google-map-loader.js.map +1 -0
  67. package/dist-client/icons/menu-icons.d.ts +6 -0
  68. package/dist-client/icons/menu-icons.js +42 -0
  69. package/dist-client/icons/menu-icons.js.map +1 -1
  70. package/dist-client/pages/component/project-update-header.d.ts +1 -0
  71. package/dist-client/pages/component/project-update-header.js +127 -0
  72. package/dist-client/pages/component/project-update-header.js.map +1 -0
  73. package/dist-client/pages/kpi-admin/dssp-kpi-list-page.d.ts +22 -0
  74. package/dist-client/pages/kpi-admin/dssp-kpi-list-page.js +57 -0
  75. package/dist-client/pages/kpi-admin/dssp-kpi-list-page.js.map +1 -0
  76. package/dist-client/pages/kpi-admin/dssp-kpi-overview.d.ts +47 -0
  77. package/dist-client/pages/kpi-admin/dssp-kpi-overview.js +393 -0
  78. package/dist-client/pages/kpi-admin/dssp-kpi-overview.js.map +1 -0
  79. package/dist-client/pages/kpi-admin/kpi-grade-2d-editor.d.ts +20 -0
  80. package/dist-client/pages/kpi-admin/kpi-grade-2d-editor.js +445 -0
  81. package/dist-client/pages/kpi-admin/kpi-grade-2d-editor.js.map +1 -0
  82. package/dist-client/pages/kpi-admin/kpi-system-guide.d.ts +18 -0
  83. package/dist-client/pages/kpi-admin/kpi-system-guide.js +696 -0
  84. package/dist-client/pages/kpi-admin/kpi-system-guide.js.map +1 -0
  85. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.d.ts +18 -0
  86. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js +259 -0
  87. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js.map +1 -0
  88. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.d.ts +22 -0
  89. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js +346 -0
  90. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js.map +1 -0
  91. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.d.ts +26 -0
  92. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js +433 -0
  93. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js.map +1 -0
  94. package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.d.ts +8 -0
  95. package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.js +78 -0
  96. package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.js.map +1 -0
  97. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.d.ts +46 -0
  98. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js +945 -0
  99. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js.map +1 -0
  100. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.d.ts +33 -0
  101. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js +231 -0
  102. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js.map +1 -0
  103. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.d.ts +37 -0
  104. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js +689 -0
  105. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js.map +1 -0
  106. package/dist-client/pages/kpi-dashboard/kpi-alert-panel.d.ts +18 -0
  107. package/dist-client/pages/kpi-dashboard/kpi-alert-panel.js +131 -0
  108. package/dist-client/pages/kpi-dashboard/kpi-alert-panel.js.map +1 -0
  109. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.d.ts +57 -0
  110. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js +865 -0
  111. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js.map +1 -0
  112. package/dist-client/pages/kpi-dashboard/kpi-dashboard.d.ts +63 -0
  113. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js +1097 -0
  114. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js.map +1 -0
  115. package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.d.ts +12 -0
  116. package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.js +82 -0
  117. package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.js.map +1 -0
  118. package/dist-client/pages/kpi-dashboard/kpi-history-viewer.d.ts +11 -0
  119. package/dist-client/pages/kpi-dashboard/kpi-history-viewer.js +65 -0
  120. package/dist-client/pages/kpi-dashboard/kpi-history-viewer.js.map +1 -0
  121. package/dist-client/pages/kpi-dashboard/kpi-list-summary.d.ts +13 -0
  122. package/dist-client/pages/kpi-dashboard/kpi-list-summary.js +115 -0
  123. package/dist-client/pages/kpi-dashboard/kpi-list-summary.js.map +1 -0
  124. package/dist-client/pages/kpi-dashboard/kpi-performance-summary.d.ts +15 -0
  125. package/dist-client/pages/kpi-dashboard/kpi-performance-summary.js +147 -0
  126. package/dist-client/pages/kpi-dashboard/kpi-performance-summary.js.map +1 -0
  127. package/dist-client/pages/kpi-dashboard/kpi-value-entry.d.ts +7 -0
  128. package/dist-client/pages/kpi-dashboard/kpi-value-entry.js +86 -0
  129. package/dist-client/pages/kpi-dashboard/kpi-value-entry.js.map +1 -0
  130. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.d.ts +57 -0
  131. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js +719 -0
  132. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js.map +1 -0
  133. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.d.ts +23 -0
  134. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js +76 -0
  135. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js.map +1 -0
  136. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.d.ts +68 -0
  137. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js +394 -0
  138. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js.map +1 -0
  139. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.d.ts +12 -0
  140. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js +174 -0
  141. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js.map +1 -0
  142. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.d.ts +40 -0
  143. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js +190 -0
  144. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js.map +1 -0
  145. package/dist-client/pages/kpi-value/kpi-value-importer.d.ts +23 -0
  146. package/dist-client/pages/kpi-value/kpi-value-importer.js +93 -0
  147. package/dist-client/pages/kpi-value/kpi-value-importer.js.map +1 -0
  148. package/dist-client/pages/kpi-value/kpi-value-list-page.d.ts +71 -0
  149. package/dist-client/pages/kpi-value/kpi-value-list-page.js +454 -0
  150. package/dist-client/pages/kpi-value/kpi-value-list-page.js.map +1 -0
  151. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.d.ts +66 -0
  152. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js +775 -0
  153. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js.map +1 -0
  154. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.d.ts +16 -0
  155. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js +337 -0
  156. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js.map +1 -0
  157. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.d.ts +21 -0
  158. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.js +335 -0
  159. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.js.map +1 -0
  160. package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.d.ts +42 -0
  161. package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.js +635 -0
  162. package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.js.map +1 -0
  163. package/dist-client/pages/sv-project-complete.d.ts +25 -0
  164. package/dist-client/pages/sv-project-complete.js +259 -0
  165. package/dist-client/pages/sv-project-complete.js.map +1 -0
  166. package/dist-client/pages/sv-project-completed-list.d.ts +27 -0
  167. package/dist-client/pages/sv-project-completed-list.js +416 -0
  168. package/dist-client/pages/sv-project-completed-list.js.map +1 -0
  169. package/dist-client/pages/sv-project-detail.d.ts +70 -0
  170. package/dist-client/pages/sv-project-detail.js +1486 -0
  171. package/dist-client/pages/sv-project-detail.js.map +1 -0
  172. package/dist-client/pages/sv-project-list.d.ts +175 -0
  173. package/dist-client/pages/sv-project-list.js +581 -0
  174. package/dist-client/pages/sv-project-list.js.map +1 -0
  175. package/dist-client/pages/sv-project-update.d.ts +91 -0
  176. package/dist-client/pages/sv-project-update.js +1151 -0
  177. package/dist-client/pages/sv-project-update.js.map +1 -0
  178. package/dist-client/pages/sv-user-management.d.ts +1 -0
  179. package/dist-client/pages/sv-user-management.js +5 -0
  180. package/dist-client/pages/sv-user-management.js.map +1 -1
  181. package/dist-client/route.d.ts +1 -1
  182. package/dist-client/route.js +50 -2
  183. package/dist-client/route.js.map +1 -1
  184. package/dist-client/shared/complete-api.d.ts +37 -0
  185. package/dist-client/shared/complete-api.js +227 -0
  186. package/dist-client/shared/complete-api.js.map +1 -0
  187. package/dist-client/shared/func.d.ts +2 -0
  188. package/dist-client/shared/func.js +22 -0
  189. package/dist-client/shared/func.js.map +1 -0
  190. package/dist-client/shared/geo-group-mapping.d.ts +25 -0
  191. package/dist-client/shared/geo-group-mapping.js +189 -0
  192. package/dist-client/shared/geo-group-mapping.js.map +1 -0
  193. package/dist-client/shared/integration-fetch.d.ts +35 -0
  194. package/dist-client/shared/integration-fetch.js +53 -0
  195. package/dist-client/shared/integration-fetch.js.map +1 -0
  196. package/dist-client/themes/dark.css +24 -24
  197. package/dist-client/themes/light.css +23 -23
  198. package/dist-client/tsconfig.tsbuildinfo +1 -1
  199. package/dist-client/viewparts/menu-tools.d.ts +40 -5
  200. package/dist-client/viewparts/menu-tools.js +317 -52
  201. package/dist-client/viewparts/menu-tools.js.map +1 -1
  202. package/dist-server/index.d.ts +2 -0
  203. package/dist-server/index.js +5 -0
  204. package/dist-server/index.js.map +1 -1
  205. package/dist-server/migrations/index.d.ts +1 -0
  206. package/dist-server/migrations/index.js +12 -0
  207. package/dist-server/migrations/index.js.map +1 -0
  208. package/dist-server/scripts/calculate-kpi-scores.d.ts +10 -0
  209. package/dist-server/scripts/calculate-kpi-scores.js +333 -0
  210. package/dist-server/scripts/calculate-kpi-scores.js.map +1 -0
  211. package/dist-server/scripts/load-grade-data-migration.d.ts +14 -0
  212. package/dist-server/scripts/load-grade-data-migration.js +279 -0
  213. package/dist-server/scripts/load-grade-data-migration.js.map +1 -0
  214. package/dist-server/scripts/propagate-parent-kpi-values.d.ts +14 -0
  215. package/dist-server/scripts/propagate-parent-kpi-values.js +786 -0
  216. package/dist-server/scripts/propagate-parent-kpi-values.js.map +1 -0
  217. package/dist-server/scripts/recalculate-by-project-name.d.ts +2 -0
  218. package/dist-server/scripts/recalculate-by-project-name.js +72 -0
  219. package/dist-server/scripts/recalculate-by-project-name.js.map +1 -0
  220. package/dist-server/service/index.d.ts +4 -0
  221. package/dist-server/service/index.js +20 -0
  222. package/dist-server/service/index.js.map +1 -0
  223. package/dist-server/service/kpi-metric-value/index.d.ts +4 -0
  224. package/dist-server/service/kpi-metric-value/index.js +8 -0
  225. package/dist-server/service/kpi-metric-value/index.js.map +1 -0
  226. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.d.ts +171 -0
  227. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js +983 -0
  228. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js.map +1 -0
  229. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.d.ts +12 -0
  230. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js +56 -0
  231. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js.map +1 -0
  232. package/dist-server/service/kpi-stat/index.d.ts +4 -0
  233. package/dist-server/service/kpi-stat/index.js +8 -0
  234. package/dist-server/service/kpi-stat/index.js.map +1 -0
  235. package/dist-server/service/kpi-stat/kpi-stat-query.d.ts +13 -0
  236. package/dist-server/service/kpi-stat/kpi-stat-query.js +751 -0
  237. package/dist-server/service/kpi-stat/kpi-stat-query.js.map +1 -0
  238. package/dist-server/service/kpi-stat/kpi-stat-types.d.ts +32 -0
  239. package/dist-server/service/kpi-stat/kpi-stat-types.js +180 -0
  240. package/dist-server/service/kpi-stat/kpi-stat-types.js.map +1 -0
  241. package/dist-server/service/kpi-value/index.d.ts +3 -0
  242. package/dist-server/service/kpi-value/index.js +7 -0
  243. package/dist-server/service/kpi-value/index.js.map +1 -0
  244. package/dist-server/service/kpi-value/kpi-value-query.d.ts +8 -0
  245. package/dist-server/service/kpi-value/kpi-value-query.js +90 -0
  246. package/dist-server/service/kpi-value/kpi-value-query.js.map +1 -0
  247. package/dist-server/tsconfig.tsbuildinfo +1 -1
  248. package/kpi-module-service-tests.md +1286 -0
  249. package/kpi-module-test-report.md +676 -0
  250. package/kpi-module-unit-test-detailed-report.md +925 -0
  251. package/kpi-module-unit-tests-detailed.md +1452 -0
  252. package/openapi/unstable.yaml +2 -2
  253. package/package.json +69 -55
  254. package/recalculate-batch.sh +64 -0
  255. package/recalculate-projects-range.sh +98 -0
  256. package/schema.graphql +3468 -457
  257. package/things-factory.config.js +12 -1
  258. package/translations/en.json +5 -1
  259. package/translations/ko.json +5 -1
  260. package/views/auth-page.html +4 -5
  261. package/views/public/home.html +5 -6
@@ -0,0 +1,689 @@
1
+ import { __decorate, __metadata } from "tslib";
2
+ import gql from 'graphql-tag';
3
+ import { LitElement, html, css, nothing } from 'lit';
4
+ import { customElement, property, state } from 'lit/decorators.js';
5
+ import { client } from '@operato/graphql';
6
+ import { ScrollbarStyles } from '@operato/styles';
7
+ import '../../../components/kpi-radar-chart.js';
8
+ import '../../../components/kpi-boxplot-chart.js';
9
+ import '../../../components/kpi-trend-chart.js';
10
+ let KpiRegionPopup = class KpiRegionPopup extends LitElement {
11
+ constructor() {
12
+ super(...arguments);
13
+ this.selectedRegion = null;
14
+ this.geoGroup = null; // 시군구 geo_group 코드 (있으면 시군구 쿼리 사용)
15
+ this.selectedCategory = '전체 KPI';
16
+ this.selectedChartType = 'boxplot';
17
+ this.selectedPeriod = '월';
18
+ this.startYearMonth = ''; // YYYY-MM 형식
19
+ this.endYearMonth = ''; // YYYY-MM 형식
20
+ this.sectorType = ''; // 유형 필터 PUBLIC | PRIVATE
21
+ this.buildingUsage = ''; // 용도 필터 RESIDENTIAL | NON_RESIDENTIAL
22
+ this.monthlyTrendData = []; // 부모에서 전달받은 월별 추이 데이터
23
+ this.totalKpiStats = []; // 전체 KPI 통계 (비교용)
24
+ this.chartData = [];
25
+ this.chartCategories = [];
26
+ this.trendData = [];
27
+ this.regionKpiStats = [];
28
+ /** 시군구 모드일 때만 사용하는 해당 시군구 월별 추이 (광역 추이 monthlyTrendData 와 별개) */
29
+ this.sigunguTrendData = [];
30
+ }
31
+ updated(changedProperties) {
32
+ const regionChanged = changedProperties.has('selectedRegion') || changedProperties.has('geoGroup');
33
+ const periodChanged = changedProperties.has('startYearMonth') || changedProperties.has('endYearMonth');
34
+ const filterChanged = changedProperties.has('sectorType') || changedProperties.has('buildingUsage');
35
+ if ((regionChanged || periodChanged || filterChanged) && this.selectedRegion) {
36
+ this.fetchRegionKpiStats();
37
+ }
38
+ // 추이 갱신: 시군구 모드면 시군구 전용 추이 조회, 아니면 부모가 준 광역(metro) 데이터로 생성
39
+ if (regionChanged ||
40
+ periodChanged ||
41
+ filterChanged ||
42
+ changedProperties.has('selectedCategory') ||
43
+ changedProperties.has('monthlyTrendData')) {
44
+ this._refreshTrend();
45
+ }
46
+ }
47
+ _refreshTrend() {
48
+ if (this.geoGroup) {
49
+ this.fetchSigunguTrend(); // 완료 후 내부에서 generateTrendData 호출
50
+ }
51
+ else {
52
+ this.generateTrendData();
53
+ }
54
+ }
55
+ /** 시군구(geo_group 코드) 단위 월별 추이 조회 → generateTrendData 재생성 */
56
+ async fetchSigunguTrend() {
57
+ var _a;
58
+ if (!this.geoGroup) {
59
+ this.sigunguTrendData = [];
60
+ this.generateTrendData();
61
+ return;
62
+ }
63
+ try {
64
+ const response = await client.query({
65
+ query: gql `
66
+ query GetSigunguTrend(
67
+ $geoGroup: String!
68
+ $startYearMonth: String
69
+ $endYearMonth: String
70
+ $sectorType: String
71
+ $buildingUsage: String
72
+ ) {
73
+ kpiZValueMonthlyTrendBySigungu(
74
+ geoGroup: $geoGroup
75
+ startYearMonth: $startYearMonth
76
+ endYearMonth: $endYearMonth
77
+ sectorType: $sectorType
78
+ buildingUsage: $buildingUsage
79
+ ) {
80
+ geoGroup
81
+ yearMonth
82
+ avgVal
83
+ projectCount
84
+ }
85
+ }
86
+ `,
87
+ variables: {
88
+ geoGroup: this.geoGroup,
89
+ startYearMonth: this.startYearMonth || null,
90
+ endYearMonth: this.endYearMonth || null,
91
+ sectorType: this.sectorType || null,
92
+ buildingUsage: this.buildingUsage || null
93
+ }
94
+ });
95
+ this.sigunguTrendData = ((_a = response.data) === null || _a === void 0 ? void 0 : _a.kpiZValueMonthlyTrendBySigungu) || [];
96
+ }
97
+ catch (e) {
98
+ console.error('시군구 추이 조회 실패:', e);
99
+ this.sigunguTrendData = [];
100
+ }
101
+ this.generateTrendData();
102
+ }
103
+ async fetchRegionKpiStats() {
104
+ if (!this.selectedRegion)
105
+ return;
106
+ try {
107
+ let regionKpiStats;
108
+ if (this.geoGroup) {
109
+ // 시군구 모드: geo_group 코드로 조회
110
+ const response = await client.query({
111
+ query: gql `
112
+ query GetSigunguKpiStats(
113
+ $geoGroup: String!
114
+ $startYearMonth: String
115
+ $endYearMonth: String
116
+ $sectorType: String
117
+ $buildingUsage: String
118
+ ) {
119
+ kpiYValueStatsByGeoGroup(
120
+ geoGroup: $geoGroup
121
+ startYearMonth: $startYearMonth
122
+ endYearMonth: $endYearMonth
123
+ sectorType: $sectorType
124
+ buildingUsage: $buildingUsage
125
+ ) {
126
+ kpiName
127
+ minVal
128
+ q1Val
129
+ medVal
130
+ q3Val
131
+ maxVal
132
+ avgVal
133
+ projectCount
134
+ }
135
+ }
136
+ `,
137
+ variables: {
138
+ geoGroup: this.geoGroup,
139
+ startYearMonth: this.startYearMonth,
140
+ endYearMonth: this.endYearMonth,
141
+ sectorType: this.sectorType || null,
142
+ buildingUsage: this.buildingUsage || null
143
+ }
144
+ });
145
+ regionKpiStats = response.data.kpiYValueStatsByGeoGroup || [];
146
+ }
147
+ else {
148
+ // 광역 모드: 광역시도 이름으로 조회
149
+ const response = await client.query({
150
+ query: gql `
151
+ query GetRegionKpiStats(
152
+ $metroArea: String!
153
+ $startYearMonth: String
154
+ $endYearMonth: String
155
+ $sectorType: String
156
+ $buildingUsage: String
157
+ ) {
158
+ kpiYValueStatsByMetroArea(
159
+ metroArea: $metroArea
160
+ startYearMonth: $startYearMonth
161
+ endYearMonth: $endYearMonth
162
+ sectorType: $sectorType
163
+ buildingUsage: $buildingUsage
164
+ ) {
165
+ kpiName
166
+ minVal
167
+ q1Val
168
+ medVal
169
+ q3Val
170
+ maxVal
171
+ avgVal
172
+ projectCount
173
+ }
174
+ }
175
+ `,
176
+ variables: {
177
+ metroArea: this.selectedRegion,
178
+ startYearMonth: this.startYearMonth,
179
+ endYearMonth: this.endYearMonth,
180
+ sectorType: this.sectorType || null,
181
+ buildingUsage: this.buildingUsage || null
182
+ }
183
+ });
184
+ regionKpiStats = response.data.kpiYValueStatsByMetroArea || [];
185
+ }
186
+ this.regionKpiStats = regionKpiStats;
187
+ this.generateChartData();
188
+ }
189
+ catch (error) {
190
+ console.error('Failed to fetch region KPI stats:', error);
191
+ this.regionKpiStats = [];
192
+ this.generateChartData();
193
+ }
194
+ }
195
+ generateChartData() {
196
+ // kpiName 표기가 'Y1. 일정성과' / '일정성과(Y1)' 등으로 환경마다 달라 전체 문자열 매칭은 깨짐.
197
+ // Y1~Y6 코드만 추출해 짧은 라벨로 매핑.
198
+ const yLabelByCode = {
199
+ Y1: '일정',
200
+ Y2: '비용',
201
+ Y3: '품질',
202
+ Y4: '안전',
203
+ Y5: '환경',
204
+ Y6: '생산성'
205
+ };
206
+ const toCategory = (kpiName) => { var _a, _b; return yLabelByCode[(_b = (_a = (kpiName || '').match(/Y[1-6]/)) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : ''] || kpiName; };
207
+ // 레이더 차트와 박스플롯은 항상 전체 Y-level KPI 표시 (selectedCategory 무시)
208
+ if (this.selectedChartType === 'radar') {
209
+ // 레이더 차트용 데이터 생성 — 지역 평균 단일 시리즈
210
+ // (전체평균 기준선은 지역 평균과 차별성이 적어 제거 → 단일 시리즈라 범례도 자동 미표시)
211
+ const data = [];
212
+ const categories = [];
213
+ // 카테고리 수집
214
+ this.regionKpiStats.forEach(stat => {
215
+ const category = toCategory(stat.kpiName);
216
+ if (!categories.includes(category)) {
217
+ categories.push(category);
218
+ }
219
+ });
220
+ // 지역 데이터 추가 (박스플롯과 동일 스케일·정밀도 — Math.round 제거해 수치 일치)
221
+ this.regionKpiStats.forEach(stat => {
222
+ const category = toCategory(stat.kpiName);
223
+ data.push({
224
+ org: this.selectedRegion || '지역',
225
+ category,
226
+ value: stat.avgVal * 100
227
+ });
228
+ });
229
+ this.chartCategories = categories.length > 0 ? categories : ['일정', '비용', '품질', '안전', '환경', '생산성'];
230
+ this.chartData = data;
231
+ }
232
+ else {
233
+ // 박스플롯용 데이터 생성
234
+ const data = [];
235
+ const categories = [];
236
+ this.regionKpiStats.forEach(stat => {
237
+ const category = toCategory(stat.kpiName);
238
+ if (!categories.includes(category)) {
239
+ categories.push(category);
240
+ }
241
+ data.push({
242
+ org: category,
243
+ min: stat.minVal * 100,
244
+ max: stat.maxVal * 100,
245
+ q1: stat.q1Val * 100,
246
+ q3: stat.q3Val * 100,
247
+ median: stat.medVal * 100,
248
+ mean: stat.avgVal * 100,
249
+ value: stat.avgVal * 100
250
+ });
251
+ });
252
+ this.chartCategories = categories.length > 0 ? categories : ['일정', '비용', '품질', '안전', '환경', '생산성'];
253
+ this.chartData = data;
254
+ }
255
+ }
256
+ generateTrendData() {
257
+ if (!this.selectedRegion)
258
+ return;
259
+ // 추이 데이터 소스: 시군구 모드면 시군구 전용 추이(sigunguTrendData), 아니면 광역(metro) 추이에서 해당 지역 필터.
260
+ const rows = this.geoGroup
261
+ ? this.sigunguTrendData
262
+ : this.monthlyTrendData.filter(d => d.geoGroup === this.selectedRegion);
263
+ // 월별 데이터를 YYYY-MM 기준 맵으로
264
+ const byMonth = new Map();
265
+ rows.forEach(d => byMonth.set(d.yearMonth, d));
266
+ // 선택 기간(start~end) 전체 월을 축으로 사용 — 데이터가 없는 월도 레코드(값 0)로 채워
267
+ // 전국 패널의 기간과 동일한 범위를 플로팅. (전체 기간 모드 등으로 start/end 가 비면 데이터 월만 사용)
268
+ const months = this.startYearMonth && this.endYearMonth
269
+ ? this.buildMonthRange(this.startYearMonth, this.endYearMonth)
270
+ : [...byMonth.keys()].sort();
271
+ let prev = null;
272
+ this.trendData = months.map(ym => {
273
+ const d = byMonth.get(ym);
274
+ const hasData = !!d;
275
+ // 데이터 없는 월은 0이 아니라 null — 0(실제 0점)과 구분하여 플로팅/표시에서 제외.
276
+ // 값은 반올림하지 않음(정수화하면 표의 소수점이 항상 .0 으로 나옴) — 표시 시 toFixed(1) 적용.
277
+ const value = hasData ? d.avgVal * 100 : null;
278
+ // 추이: 무데이터 'none', 직전 데이터 없으면(첫 점) 'flat', 그 외 실제 증감 비교
279
+ let trendDir;
280
+ if (!hasData) {
281
+ trendDir = 'none';
282
+ }
283
+ else if (prev === null) {
284
+ trendDir = 'flat';
285
+ }
286
+ else {
287
+ trendDir = value > prev ? 'up' : value < prev ? 'down' : 'flat';
288
+ }
289
+ if (hasData)
290
+ prev = value;
291
+ return {
292
+ date: ym, // YYYY-MM 형식
293
+ value,
294
+ color: value == null ? undefined : value > 60 ? '#4caf50' : value > 40 ? '#ff9800' : '#f44336',
295
+ trendDir
296
+ };
297
+ });
298
+ }
299
+ /** start~end(YYYY-MM) 사이 모든 월을 순서대로 반환. 잘못된 범위는 안전하게 종료. */
300
+ buildMonthRange(start, end) {
301
+ const [sy, sm] = start.split('-').map(Number);
302
+ const [ey, em] = end.split('-').map(Number);
303
+ if (!sy || !sm || !ey || !em)
304
+ return [];
305
+ const out = [];
306
+ let y = sy;
307
+ let m = sm;
308
+ for (let i = 0; i < 240; i++) {
309
+ out.push(`${y}-${String(m).padStart(2, '0')}`);
310
+ if (y === ey && m === em)
311
+ break;
312
+ if (y > ey || (y === ey && m > em))
313
+ break; // start > end 방어
314
+ m++;
315
+ if (m > 12) {
316
+ m = 1;
317
+ y++;
318
+ }
319
+ }
320
+ return out;
321
+ }
322
+ onClose() {
323
+ this.dispatchEvent(new CustomEvent('popup-close', {
324
+ bubbles: true,
325
+ composed: true
326
+ }));
327
+ }
328
+ onChartTypeChange(type) {
329
+ this.selectedChartType = type;
330
+ this.generateChartData();
331
+ }
332
+ render() {
333
+ if (!this.selectedRegion) {
334
+ return nothing;
335
+ }
336
+ return html `
337
+ <div class="popup-header">
338
+ <div class="popup-title">${this.selectedRegion} KPI</div>
339
+ <button class="popup-close" @click=${this.onClose}>×</button>
340
+ </div>
341
+ <div class="popup-content">
342
+ <!-- 종합 성과 -->
343
+ <div class="chart-section">
344
+ <div class="sub-title">종합 성과</div>
345
+ <div class="chart-toggle">
346
+ <button
347
+ class="toggle-button ${this.selectedChartType === 'boxplot' ? 'active' : ''}"
348
+ @click=${() => this.onChartTypeChange('boxplot')}
349
+ >
350
+ 박스플롯
351
+ </button>
352
+ <button
353
+ class="toggle-button ${this.selectedChartType === 'radar' ? 'active' : ''}"
354
+ @click=${() => this.onChartTypeChange('radar')}
355
+ >
356
+ 레이더차트
357
+ </button>
358
+ </div>
359
+ <div class="chart-container">
360
+ ${this.chartData.length === 0
361
+ ? html `<div
362
+ style="display:flex;align-items:center;justify-content:center;height:100%;color:#888;font-size:0.9rem;"
363
+ >
364
+ 선택한 기간에 데이터가 없습니다.
365
+ </div>`
366
+ : this.selectedChartType === 'boxplot'
367
+ ? html `
368
+ <sv-kpi-boxplot-chart
369
+ .data=${this.chartData}
370
+ .groups=${this.chartCategories}
371
+ .valueKey=${'value'}
372
+ .valueLabel=${this.selectedRegion || '지역'}
373
+ .showStatus=${false}
374
+ ></sv-kpi-boxplot-chart>
375
+ `
376
+ : html `
377
+ <sv-kpi-radar-chart
378
+ .data=${this.chartData}
379
+ .categories=${this.chartCategories}
380
+ .valueKey=${'value'}
381
+ ></sv-kpi-radar-chart>
382
+ `}
383
+ </div>
384
+ </div>
385
+
386
+ <!-- 기간별 성과 추이 -->
387
+ <div class="trend-section">
388
+ <div class="sub-title">기간별 성과 추이</div>
389
+ <div class="trend-chart-container" style="height: 200px; margin-bottom: 16px;">
390
+ <sv-kpi-trend-chart
391
+ .data=${this.trendData}
392
+ .lineColor=${'#2196f3'}
393
+ .strokeWidth=${2}
394
+ .showPoints=${false}
395
+ .pointRadius=${4}
396
+ ></sv-kpi-trend-chart>
397
+ </div>
398
+ <div class="trend-table">
399
+ <table style="width: 100%; border-collapse: collapse; font-size: 0.85rem;">
400
+ <thead style="position: sticky; top: 0; background: white; z-index: 1;">
401
+ <tr style="border-bottom: 2px solid #f1f3f4; background-color: #f8f9fa;">
402
+ <th style="padding: 8px; text-align: left; font-weight: 600;">날짜</th>
403
+ <th style="padding: 8px; text-align: right; font-weight: 600;">성과</th>
404
+ <th style="padding: 8px; text-align: center; font-weight: 600;">추이</th>
405
+ </tr>
406
+ </thead>
407
+ <tbody>
408
+ ${[...this.trendData].reverse().map((item) => html `
409
+ <tr style="border-bottom: 1px solid #f1f3f4;">
410
+ <td style="padding: 8px; font-size: 0.85rem;">${item.date}</td>
411
+ <td style="padding: 8px; text-align: right; font-size: 0.85rem; font-weight: 600;">
412
+ ${item.value != null ? item.value.toFixed(1) : '-'}
413
+ </td>
414
+ <td style="padding: 8px; text-align: center; font-size: 0.85rem;">
415
+ <span style="color: ${item.trendDir === 'up' ? '#e53935' : item.trendDir === 'down' ? '#2196f3' : '#999'};">
416
+ ${item.trendDir === 'up' ? '▲' : item.trendDir === 'down' ? '▼' : '–'}
417
+ </span>
418
+ </td>
419
+ </tr>
420
+ `)}
421
+ </tbody>
422
+ </table>
423
+ </div>
424
+ </div>
425
+ </div>
426
+ `;
427
+ }
428
+ };
429
+ KpiRegionPopup.styles = [
430
+ ScrollbarStyles,
431
+ css `
432
+ :host {
433
+ display: block;
434
+ position: absolute;
435
+ top: 0;
436
+ left: 402px;
437
+ width: 400px;
438
+ height: 100%;
439
+ background: #fff;
440
+ border-right: 1px solid #e0e0e0;
441
+ z-index: 1000;
442
+ overflow: hidden;
443
+ display: flex;
444
+ flex-direction: column;
445
+ box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
446
+ }
447
+ .popup-content {
448
+ padding: 20px;
449
+ overflow-y: auto;
450
+ flex: 1;
451
+ }
452
+ .popup-header {
453
+ display: flex;
454
+ justify-content: space-between;
455
+ align-items: center;
456
+ padding: 20px;
457
+ border-bottom: 1px solid #e0e0e0;
458
+ background: #fff;
459
+ height: 70px;
460
+ box-sizing: border-box;
461
+ }
462
+ .popup-title {
463
+ font-size: 1.2rem;
464
+ font-weight: bold;
465
+ color: #333;
466
+ }
467
+ .popup-close {
468
+ width: 32px;
469
+ height: 32px;
470
+ border: none;
471
+ background: #fff;
472
+ border-radius: 50%;
473
+ cursor: pointer;
474
+ display: flex;
475
+ align-items: center;
476
+ justify-content: center;
477
+ font-size: 1.2rem;
478
+ color: #666;
479
+ transition: all 0.2s;
480
+ }
481
+ .popup-close:hover {
482
+ background: #e9ecef;
483
+ color: #333;
484
+ }
485
+ .sub-title {
486
+ font-size: 1rem;
487
+ font-weight: 600;
488
+ margin-bottom: 16px;
489
+ color: #495057;
490
+ }
491
+ .chart-section {
492
+ background: #f8f9fa;
493
+ border-radius: 8px;
494
+ padding: 16px;
495
+ margin-bottom: 20px;
496
+ }
497
+ .chart-toggle {
498
+ display: flex;
499
+ gap: 8px;
500
+ margin-bottom: 16px;
501
+ }
502
+ .toggle-button {
503
+ padding: 8px 16px;
504
+ border: 1px solid #ced4da;
505
+ background: #fff;
506
+ border-radius: 6px;
507
+ cursor: pointer;
508
+ font-size: 0.9rem;
509
+ transition: all 0.2s;
510
+ }
511
+ .toggle-button.active {
512
+ background: #667eea;
513
+ color: white;
514
+ border-color: #667eea;
515
+ }
516
+ .chart-container {
517
+ height: 300px;
518
+ display: flex;
519
+ align-items: center;
520
+ justify-content: center;
521
+ background: white;
522
+ border-radius: 6px;
523
+ border: 1px solid #e9ecef;
524
+ }
525
+ .period-selector {
526
+ display: flex;
527
+ gap: 8px;
528
+ margin-bottom: 16px;
529
+ }
530
+ .period-button {
531
+ padding: 6px 12px;
532
+ border: 1px solid #ced4da;
533
+ background: #fff;
534
+ border-radius: 4px;
535
+ cursor: pointer;
536
+ font-size: 0.85rem;
537
+ transition: all 0.2s;
538
+ }
539
+ .period-button.active {
540
+ background: #667eea;
541
+ color: white;
542
+ border-color: #667eea;
543
+ }
544
+ .date-range-selector {
545
+ display: flex;
546
+ align-items: center;
547
+ gap: 8px;
548
+ margin-bottom: 16px;
549
+ }
550
+ .date-select {
551
+ padding: 6px 12px;
552
+ border: 1px solid #ced4da;
553
+ border-radius: 4px;
554
+ background: white;
555
+ font-size: 0.9rem;
556
+ }
557
+ `
558
+ ];
559
+ // geo_group → 광역시도 매핑 (시군구 모드에서 추이 데이터 조회용)
560
+ KpiRegionPopup.GEO_TO_METRO = {
561
+ '01': '서울특별시',
562
+ '02': '서울특별시',
563
+ '03': '서울특별시',
564
+ '04': '서울특별시',
565
+ '05': '서울특별시',
566
+ '06': '서울특별시',
567
+ '07': '서울특별시',
568
+ '08': '서울특별시',
569
+ '10': '경기도',
570
+ '11': '경기도',
571
+ '12': '경기도',
572
+ '13': '경기도',
573
+ '14': '경기도',
574
+ '15': '경기도',
575
+ '16': '경기도',
576
+ '17': '경기도',
577
+ '18': '경기도',
578
+ '21': '인천광역시',
579
+ '22': '인천광역시',
580
+ '23': '인천광역시',
581
+ '24': '강원도',
582
+ '25': '강원도',
583
+ '26': '강원도',
584
+ '33': '강원도',
585
+ '27': '충청북도',
586
+ '28': '충청북도',
587
+ '29': '충청북도',
588
+ '30': '세종특별자치시',
589
+ '31': '충청남도',
590
+ '32': '충청남도',
591
+ '34': '대전광역시',
592
+ '35': '대전광역시',
593
+ '36': '경상북도',
594
+ '37': '경상북도',
595
+ '38': '경상북도',
596
+ '39': '경상북도',
597
+ '40': '경상북도',
598
+ '41': '대구광역시',
599
+ '42': '대구광역시',
600
+ '43': '대구광역시',
601
+ '44': '울산광역시',
602
+ '45': '울산광역시',
603
+ '46': '부산광역시',
604
+ '47': '부산광역시',
605
+ '48': '부산광역시',
606
+ '49': '부산광역시',
607
+ '50': '경상남도',
608
+ '51': '경상남도',
609
+ '52': '경상남도',
610
+ '53': '경상남도',
611
+ '54': '전라북도',
612
+ '55': '전라북도',
613
+ '56': '전라북도',
614
+ '57': '전라남도',
615
+ '58': '전라남도',
616
+ '59': '전라남도',
617
+ '61': '광주광역시',
618
+ '62': '광주광역시',
619
+ '63': '제주특별자치도'
620
+ };
621
+ __decorate([
622
+ property({ type: String }),
623
+ __metadata("design:type", Object)
624
+ ], KpiRegionPopup.prototype, "selectedRegion", void 0);
625
+ __decorate([
626
+ property({ type: String }),
627
+ __metadata("design:type", Object)
628
+ ], KpiRegionPopup.prototype, "geoGroup", void 0);
629
+ __decorate([
630
+ property({ type: String }),
631
+ __metadata("design:type", Object)
632
+ ], KpiRegionPopup.prototype, "selectedCategory", void 0);
633
+ __decorate([
634
+ property({ type: String }),
635
+ __metadata("design:type", Object)
636
+ ], KpiRegionPopup.prototype, "selectedChartType", void 0);
637
+ __decorate([
638
+ property({ type: String }),
639
+ __metadata("design:type", Object)
640
+ ], KpiRegionPopup.prototype, "selectedPeriod", void 0);
641
+ __decorate([
642
+ property({ type: String }),
643
+ __metadata("design:type", Object)
644
+ ], KpiRegionPopup.prototype, "startYearMonth", void 0);
645
+ __decorate([
646
+ property({ type: String }),
647
+ __metadata("design:type", Object)
648
+ ], KpiRegionPopup.prototype, "endYearMonth", void 0);
649
+ __decorate([
650
+ property({ type: String }),
651
+ __metadata("design:type", Object)
652
+ ], KpiRegionPopup.prototype, "sectorType", void 0);
653
+ __decorate([
654
+ property({ type: String }),
655
+ __metadata("design:type", Object)
656
+ ], KpiRegionPopup.prototype, "buildingUsage", void 0);
657
+ __decorate([
658
+ property({ type: Array }),
659
+ __metadata("design:type", Array)
660
+ ], KpiRegionPopup.prototype, "monthlyTrendData", void 0);
661
+ __decorate([
662
+ property({ type: Array }),
663
+ __metadata("design:type", Array)
664
+ ], KpiRegionPopup.prototype, "totalKpiStats", void 0);
665
+ __decorate([
666
+ state(),
667
+ __metadata("design:type", Array)
668
+ ], KpiRegionPopup.prototype, "chartData", void 0);
669
+ __decorate([
670
+ state(),
671
+ __metadata("design:type", Array)
672
+ ], KpiRegionPopup.prototype, "chartCategories", void 0);
673
+ __decorate([
674
+ state(),
675
+ __metadata("design:type", Array)
676
+ ], KpiRegionPopup.prototype, "trendData", void 0);
677
+ __decorate([
678
+ state(),
679
+ __metadata("design:type", Array)
680
+ ], KpiRegionPopup.prototype, "regionKpiStats", void 0);
681
+ __decorate([
682
+ state(),
683
+ __metadata("design:type", Array)
684
+ ], KpiRegionPopup.prototype, "sigunguTrendData", void 0);
685
+ KpiRegionPopup = __decorate([
686
+ customElement('kpi-region-popup')
687
+ ], KpiRegionPopup);
688
+ export { KpiRegionPopup };
689
+ //# sourceMappingURL=kpi-region-popup.js.map