@dssp/dkpi 1.0.0-alpha.7 → 1.0.0-alpha.71

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 (232) hide show
  1. package/KPI-STATISTICS-SERVICE.md +233 -0
  2. package/_index.html +0 -5
  3. package/assets/favicon.ico +0 -0
  4. package/assets/images/project-image.png +0 -0
  5. package/assets/manifest/apple-1024.png +0 -0
  6. package/assets/manifest/apple-120.png +0 -0
  7. package/assets/manifest/apple-152.png +0 -0
  8. package/assets/manifest/apple-167.png +0 -0
  9. package/assets/manifest/apple-180.png +0 -0
  10. package/assets/manifest/apple-touch-icon.png +0 -0
  11. package/assets/manifest/badge-128x128.png +0 -0
  12. package/assets/manifest/chrome-splashscreen-icon-384x384.png +0 -0
  13. package/assets/manifest/chrome-touch-icon-192x192.png +0 -0
  14. package/assets/manifest/icon-128x128.png +0 -0
  15. package/assets/manifest/icon-192x192.png +0 -0
  16. package/assets/manifest/icon-512x512.png +0 -0
  17. package/assets/manifest/icon-72x72.png +0 -0
  18. package/assets/manifest/icon-96x96.png +0 -0
  19. package/assets/manifest/image-metaog.png +0 -0
  20. package/assets/manifest/maskable_icon.png +0 -0
  21. package/assets/manifest/ms-icon-144x144.png +0 -0
  22. package/assets/manifest/ms-touch-icon-144x144-precomposed.png +0 -0
  23. package/assets/videos/intro.mp4 +0 -0
  24. package/dist-client/bootstrap.js +64 -4
  25. package/dist-client/bootstrap.js.map +1 -1
  26. package/dist-client/components/kpi-2d-lookup-chart.d.ts +43 -0
  27. package/dist-client/components/kpi-2d-lookup-chart.js +253 -0
  28. package/dist-client/components/kpi-2d-lookup-chart.js.map +1 -0
  29. package/dist-client/components/kpi-boxplot-chart.d.ts +24 -0
  30. package/dist-client/components/kpi-boxplot-chart.js +291 -0
  31. package/dist-client/components/kpi-boxplot-chart.js.map +1 -0
  32. package/dist-client/components/kpi-lookup-chart.d.ts +53 -0
  33. package/dist-client/components/kpi-lookup-chart.js +430 -0
  34. package/dist-client/components/kpi-lookup-chart.js.map +1 -0
  35. package/dist-client/components/kpi-mini-trend-chart.d.ts +14 -0
  36. package/dist-client/components/kpi-mini-trend-chart.js +148 -0
  37. package/dist-client/components/kpi-mini-trend-chart.js.map +1 -0
  38. package/dist-client/components/kpi-radar-chart.d.ts +17 -0
  39. package/dist-client/components/kpi-radar-chart.js +259 -0
  40. package/dist-client/components/kpi-radar-chart.js.map +1 -0
  41. package/dist-client/components/kpi-single-boxplot-chart.d.ts +24 -0
  42. package/dist-client/components/kpi-single-boxplot-chart.js +391 -0
  43. package/dist-client/components/kpi-single-boxplot-chart.js.map +1 -0
  44. package/dist-client/components/kpi-trend-chart.d.ts +25 -0
  45. package/dist-client/components/kpi-trend-chart.js +220 -0
  46. package/dist-client/components/kpi-trend-chart.js.map +1 -0
  47. package/dist-client/components/sv-pagenation-control.d.ts +18 -0
  48. package/dist-client/components/sv-pagenation-control.js +142 -0
  49. package/dist-client/components/sv-pagenation-control.js.map +1 -0
  50. package/dist-client/google-map/common-google-map.d.ts +35 -0
  51. package/dist-client/google-map/common-google-map.js +345 -0
  52. package/dist-client/google-map/common-google-map.js.map +1 -0
  53. package/dist-client/google-map/google-map-loader.d.ts +6 -0
  54. package/dist-client/google-map/google-map-loader.js +23 -0
  55. package/dist-client/google-map/google-map-loader.js.map +1 -0
  56. package/dist-client/icons/menu-icons.d.ts +6 -0
  57. package/dist-client/icons/menu-icons.js +42 -0
  58. package/dist-client/icons/menu-icons.js.map +1 -1
  59. package/dist-client/pages/kpi-admin/dssp-kpi-list-page.d.ts +22 -0
  60. package/dist-client/pages/kpi-admin/dssp-kpi-list-page.js +57 -0
  61. package/dist-client/pages/kpi-admin/dssp-kpi-list-page.js.map +1 -0
  62. package/dist-client/pages/kpi-admin/dssp-kpi-overview.d.ts +46 -0
  63. package/dist-client/pages/kpi-admin/dssp-kpi-overview.js +378 -0
  64. package/dist-client/pages/kpi-admin/dssp-kpi-overview.js.map +1 -0
  65. package/dist-client/pages/kpi-admin/kpi-grade-2d-editor.d.ts +20 -0
  66. package/dist-client/pages/kpi-admin/kpi-grade-2d-editor.js +445 -0
  67. package/dist-client/pages/kpi-admin/kpi-grade-2d-editor.js.map +1 -0
  68. package/dist-client/pages/kpi-admin/kpi-system-guide.d.ts +18 -0
  69. package/dist-client/pages/kpi-admin/kpi-system-guide.js +535 -0
  70. package/dist-client/pages/kpi-admin/kpi-system-guide.js.map +1 -0
  71. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.d.ts +18 -0
  72. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js +259 -0
  73. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js.map +1 -0
  74. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.d.ts +22 -0
  75. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js +346 -0
  76. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js.map +1 -0
  77. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.d.ts +26 -0
  78. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js +433 -0
  79. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js.map +1 -0
  80. package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.d.ts +8 -0
  81. package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.js +78 -0
  82. package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.js.map +1 -0
  83. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.d.ts +38 -0
  84. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js +851 -0
  85. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js.map +1 -0
  86. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.d.ts +42 -0
  87. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js +316 -0
  88. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js.map +1 -0
  89. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.d.ts +28 -0
  90. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js +497 -0
  91. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js.map +1 -0
  92. package/dist-client/pages/kpi-dashboard/kpi-alert-panel.d.ts +18 -0
  93. package/dist-client/pages/kpi-dashboard/kpi-alert-panel.js +131 -0
  94. package/dist-client/pages/kpi-dashboard/kpi-alert-panel.js.map +1 -0
  95. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.d.ts +52 -0
  96. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js +798 -0
  97. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js.map +1 -0
  98. package/dist-client/pages/kpi-dashboard/kpi-dashboard.d.ts +63 -0
  99. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js +1089 -0
  100. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js.map +1 -0
  101. package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.d.ts +12 -0
  102. package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.js +82 -0
  103. package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.js.map +1 -0
  104. package/dist-client/pages/kpi-dashboard/kpi-history-viewer.d.ts +11 -0
  105. package/dist-client/pages/kpi-dashboard/kpi-history-viewer.js +65 -0
  106. package/dist-client/pages/kpi-dashboard/kpi-history-viewer.js.map +1 -0
  107. package/dist-client/pages/kpi-dashboard/kpi-list-summary.d.ts +13 -0
  108. package/dist-client/pages/kpi-dashboard/kpi-list-summary.js +115 -0
  109. package/dist-client/pages/kpi-dashboard/kpi-list-summary.js.map +1 -0
  110. package/dist-client/pages/kpi-dashboard/kpi-performance-summary.d.ts +15 -0
  111. package/dist-client/pages/kpi-dashboard/kpi-performance-summary.js +147 -0
  112. package/dist-client/pages/kpi-dashboard/kpi-performance-summary.js.map +1 -0
  113. package/dist-client/pages/kpi-dashboard/kpi-value-entry.d.ts +7 -0
  114. package/dist-client/pages/kpi-dashboard/kpi-value-entry.js +86 -0
  115. package/dist-client/pages/kpi-dashboard/kpi-value-entry.js.map +1 -0
  116. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.d.ts +57 -0
  117. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js +719 -0
  118. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js.map +1 -0
  119. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.d.ts +23 -0
  120. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js +76 -0
  121. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js.map +1 -0
  122. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.d.ts +68 -0
  123. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js +380 -0
  124. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js.map +1 -0
  125. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.d.ts +12 -0
  126. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js +174 -0
  127. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js.map +1 -0
  128. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.d.ts +40 -0
  129. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js +190 -0
  130. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js.map +1 -0
  131. package/dist-client/pages/kpi-value/kpi-value-importer.d.ts +23 -0
  132. package/dist-client/pages/kpi-value/kpi-value-importer.js +93 -0
  133. package/dist-client/pages/kpi-value/kpi-value-importer.js.map +1 -0
  134. package/dist-client/pages/kpi-value/kpi-value-list-page.d.ts +71 -0
  135. package/dist-client/pages/kpi-value/kpi-value-list-page.js +464 -0
  136. package/dist-client/pages/kpi-value/kpi-value-list-page.js.map +1 -0
  137. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.d.ts +14 -0
  138. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js +339 -0
  139. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js.map +1 -0
  140. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.d.ts +14 -0
  141. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js +276 -0
  142. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js.map +1 -0
  143. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.d.ts +18 -0
  144. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.js +307 -0
  145. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.js.map +1 -0
  146. package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.d.ts +18 -0
  147. package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.js +433 -0
  148. package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.js.map +1 -0
  149. package/dist-client/pages/sv-project-complete.d.ts +21 -0
  150. package/dist-client/pages/sv-project-complete.js +213 -0
  151. package/dist-client/pages/sv-project-complete.js.map +1 -0
  152. package/dist-client/pages/sv-project-completed-list.d.ts +27 -0
  153. package/dist-client/pages/sv-project-completed-list.js +416 -0
  154. package/dist-client/pages/sv-project-completed-list.js.map +1 -0
  155. package/dist-client/pages/sv-project-detail.d.ts +46 -0
  156. package/dist-client/pages/sv-project-detail.js +1236 -0
  157. package/dist-client/pages/sv-project-detail.js.map +1 -0
  158. package/dist-client/pages/sv-project-list.d.ts +165 -0
  159. package/dist-client/pages/sv-project-list.js +488 -0
  160. package/dist-client/pages/sv-project-list.js.map +1 -0
  161. package/dist-client/route.d.ts +1 -1
  162. package/dist-client/route.js +35 -1
  163. package/dist-client/route.js.map +1 -1
  164. package/dist-client/shared/complete-api.d.ts +8 -0
  165. package/dist-client/shared/complete-api.js +177 -0
  166. package/dist-client/shared/complete-api.js.map +1 -0
  167. package/dist-client/shared/func.d.ts +2 -0
  168. package/dist-client/shared/func.js +22 -0
  169. package/dist-client/shared/func.js.map +1 -0
  170. package/dist-client/themes/dark.css +24 -24
  171. package/dist-client/themes/light.css +23 -23
  172. package/dist-client/tsconfig.tsbuildinfo +1 -1
  173. package/dist-client/viewparts/menu-tools.d.ts +40 -5
  174. package/dist-client/viewparts/menu-tools.js +289 -34
  175. package/dist-client/viewparts/menu-tools.js.map +1 -1
  176. package/dist-server/index.d.ts +2 -0
  177. package/dist-server/index.js +5 -0
  178. package/dist-server/index.js.map +1 -1
  179. package/dist-server/migrations/index.d.ts +1 -0
  180. package/dist-server/migrations/index.js +12 -0
  181. package/dist-server/migrations/index.js.map +1 -0
  182. package/dist-server/scripts/calculate-kpi-scores.d.ts +10 -0
  183. package/dist-server/scripts/calculate-kpi-scores.js +333 -0
  184. package/dist-server/scripts/calculate-kpi-scores.js.map +1 -0
  185. package/dist-server/scripts/load-grade-data-migration.d.ts +14 -0
  186. package/dist-server/scripts/load-grade-data-migration.js +279 -0
  187. package/dist-server/scripts/load-grade-data-migration.js.map +1 -0
  188. package/dist-server/scripts/propagate-parent-kpi-values.d.ts +14 -0
  189. package/dist-server/scripts/propagate-parent-kpi-values.js +786 -0
  190. package/dist-server/scripts/propagate-parent-kpi-values.js.map +1 -0
  191. package/dist-server/scripts/recalculate-by-project-name.d.ts +2 -0
  192. package/dist-server/scripts/recalculate-by-project-name.js +72 -0
  193. package/dist-server/scripts/recalculate-by-project-name.js.map +1 -0
  194. package/dist-server/service/index.d.ts +4 -0
  195. package/dist-server/service/index.js +20 -0
  196. package/dist-server/service/index.js.map +1 -0
  197. package/dist-server/service/kpi-metric-value/index.d.ts +4 -0
  198. package/dist-server/service/kpi-metric-value/index.js +8 -0
  199. package/dist-server/service/kpi-metric-value/index.js.map +1 -0
  200. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.d.ts +74 -0
  201. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js +687 -0
  202. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js.map +1 -0
  203. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.d.ts +7 -0
  204. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js +52 -0
  205. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js.map +1 -0
  206. package/dist-server/service/kpi-stat/index.d.ts +4 -0
  207. package/dist-server/service/kpi-stat/index.js +8 -0
  208. package/dist-server/service/kpi-stat/index.js.map +1 -0
  209. package/dist-server/service/kpi-stat/kpi-stat-query.d.ts +12 -0
  210. package/dist-server/service/kpi-stat/kpi-stat-query.js +662 -0
  211. package/dist-server/service/kpi-stat/kpi-stat-query.js.map +1 -0
  212. package/dist-server/service/kpi-stat/kpi-stat-types.d.ts +32 -0
  213. package/dist-server/service/kpi-stat/kpi-stat-types.js +180 -0
  214. package/dist-server/service/kpi-stat/kpi-stat-types.js.map +1 -0
  215. package/dist-server/service/kpi-value/index.d.ts +3 -0
  216. package/dist-server/service/kpi-value/index.js +7 -0
  217. package/dist-server/service/kpi-value/index.js.map +1 -0
  218. package/dist-server/service/kpi-value/kpi-value-query.d.ts +8 -0
  219. package/dist-server/service/kpi-value/kpi-value-query.js +69 -0
  220. package/dist-server/service/kpi-value/kpi-value-query.js.map +1 -0
  221. package/dist-server/tsconfig.tsbuildinfo +1 -1
  222. package/kpi-module-service-tests.md +1286 -0
  223. package/kpi-module-test-report.md +676 -0
  224. package/kpi-module-unit-test-detailed-report.md +925 -0
  225. package/kpi-module-unit-tests-detailed.md +1452 -0
  226. package/package.json +67 -55
  227. package/recalculate-batch.sh +64 -0
  228. package/recalculate-projects-range.sh +98 -0
  229. package/schema.graphql +2514 -455
  230. package/things-factory.config.js +11 -1
  231. package/views/auth-page.html +0 -1
  232. package/views/public/home.html +0 -1
@@ -0,0 +1,851 @@
1
+ import { __decorate, __metadata } from "tslib";
2
+ import gql from 'graphql-tag';
3
+ import { LitElement, html, css } from 'lit';
4
+ import { customElement, property, state } from 'lit/decorators.js';
5
+ import { ScrollbarStyles } from '@operato/styles';
6
+ import { client } from '@operato/graphql';
7
+ import '../../../components/kpi-radar-chart.js';
8
+ import '../../../components/kpi-boxplot-chart.js';
9
+ import '../../../components/kpi-mini-trend-chart.js';
10
+ let KpiLeftPanel = class KpiLeftPanel extends LitElement {
11
+ constructor() {
12
+ super(...arguments);
13
+ this.selectedCategory = '전체 KPI';
14
+ this.selectedChartType = 'boxplot';
15
+ this.mapData = [];
16
+ this.startYearMonth = ''; // YYYY-MM 형식
17
+ this.endYearMonth = ''; // YYYY-MM 형식
18
+ this.sectorType = '';
19
+ this.buildingUsage = '';
20
+ this.isAllPeriod = false;
21
+ this.chartData = [];
22
+ this.chartCategories = [];
23
+ this.kpiYComprehensiveStats = [];
24
+ this.regionalKpiStats = [];
25
+ this.monthlyTrendData = [];
26
+ // 행정안전부 행정구역코드 순서
27
+ this.regionOrder = [
28
+ '서울특별시',
29
+ '부산광역시',
30
+ '대구광역시',
31
+ '인천광역시',
32
+ '광주광역시',
33
+ '대전광역시',
34
+ '울산광역시',
35
+ '세종특별자치시',
36
+ '경기도',
37
+ '강원도',
38
+ '충청북도',
39
+ '충청남도',
40
+ '전라북도',
41
+ '전라남도',
42
+ '경상북도',
43
+ '경상남도',
44
+ '제주특별자치도'
45
+ ];
46
+ }
47
+ connectedCallback() {
48
+ super.connectedCallback();
49
+ // 기간 초기값이 비어있으면 현재 월 기준 12개월로 설정
50
+ if (!this.startYearMonth || !this.endYearMonth) {
51
+ const now = new Date();
52
+ const end = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`;
53
+ const start = new Date(now);
54
+ start.setMonth(start.getMonth() - 11);
55
+ const startStr = `${start.getFullYear()}-${String(start.getMonth() + 1).padStart(2, '0')}`;
56
+ this.startYearMonth = startStr;
57
+ this.endYearMonth = end;
58
+ }
59
+ this.fetchKpiYComprehensiveStats();
60
+ this.fetchRegionalKpiStats();
61
+ this.fetchMonthlyTrendData();
62
+ }
63
+ updated(changedProperties) {
64
+ // selectedCategory, startYearMonth, endYearMonth가 변경되면 데이터 다시 가져오기
65
+ if (changedProperties.has('selectedCategory') ||
66
+ changedProperties.has('startYearMonth') ||
67
+ changedProperties.has('endYearMonth') ||
68
+ changedProperties.has('sectorType') ||
69
+ changedProperties.has('buildingUsage')) {
70
+ // 기간 정보가 설정되어 있을 때만 조회 (빈 문자열이면 전체 기간)
71
+ if (this.startYearMonth !== undefined && this.endYearMonth !== undefined) {
72
+ this.fetchKpiYComprehensiveStats();
73
+ this.fetchRegionalKpiStats();
74
+ this.fetchMonthlyTrendData();
75
+ }
76
+ }
77
+ }
78
+ async fetchKpiYComprehensiveStats() {
79
+ try {
80
+ const response = await client.query({
81
+ query: gql `
82
+ query GetKpiYValueComprehensiveStats(
83
+ $startYearMonth: String
84
+ $endYearMonth: String
85
+ $sectorType: String
86
+ $buildingUsage: String
87
+ ) {
88
+ totalKpiYValueComprehensiveStats(
89
+ startYearMonth: $startYearMonth
90
+ endYearMonth: $endYearMonth
91
+ sectorType: $sectorType
92
+ buildingUsage: $buildingUsage
93
+ ) {
94
+ kpiName
95
+ minVal
96
+ q1Val
97
+ medVal
98
+ q3Val
99
+ maxVal
100
+ avgVal
101
+ }
102
+ }
103
+ `,
104
+ variables: {
105
+ startYearMonth: this.startYearMonth,
106
+ endYearMonth: this.endYearMonth,
107
+ sectorType: this.sectorType || null,
108
+ buildingUsage: this.buildingUsage || null
109
+ }
110
+ });
111
+ this.kpiYComprehensiveStats = response.data.totalKpiYValueComprehensiveStats || [];
112
+ // 부모에게 전체 Y-level KPI 통계 전달
113
+ this.dispatchEvent(new CustomEvent('total-kpi-y-stats-loaded', {
114
+ detail: { data: this.kpiYComprehensiveStats },
115
+ bubbles: true,
116
+ composed: true
117
+ }));
118
+ this.generateChartData();
119
+ }
120
+ catch (error) {
121
+ console.error('Failed to fetch KPI Y comprehensive stats:', error);
122
+ this.kpiYComprehensiveStats = [];
123
+ this.generateChartData();
124
+ }
125
+ }
126
+ async fetchRegionalKpiStats() {
127
+ try {
128
+ // selectedCategory에 따라 kpiName 결정
129
+ const categoryToKpiName = {
130
+ '전체 KPI': null,
131
+ '일정 성과': 'Y1. 일정성과',
132
+ '비용 성과': 'Y2. 비용성과',
133
+ '품질 성과': 'Y3. 품질성과',
134
+ '안전 성과': 'Y4. 안전성과',
135
+ '환경 성과': 'Y5. 환경성과',
136
+ '생산성 성과': 'Y6. 생산성성과'
137
+ };
138
+ const kpiName = categoryToKpiName[this.selectedCategory];
139
+ const response = await client.query({
140
+ query: gql `
141
+ query GetRegionalKpiStats(
142
+ $kpiName: String
143
+ $startYearMonth: String
144
+ $endYearMonth: String
145
+ $sectorType: String
146
+ $buildingUsage: String
147
+ ) {
148
+ kpiZValueComprehensiveStatsByGeoGroup(
149
+ kpiName: $kpiName
150
+ startYearMonth: $startYearMonth
151
+ endYearMonth: $endYearMonth
152
+ sectorType: $sectorType
153
+ buildingUsage: $buildingUsage
154
+ ) {
155
+ geoGroup
156
+ avgVal
157
+ minVal
158
+ maxVal
159
+ projectCount
160
+ }
161
+ }
162
+ `,
163
+ variables: {
164
+ kpiName,
165
+ startYearMonth: this.startYearMonth,
166
+ endYearMonth: this.endYearMonth,
167
+ sectorType: this.sectorType || null,
168
+ buildingUsage: this.buildingUsage || null
169
+ }
170
+ });
171
+ this.regionalKpiStats = response.data.kpiZValueComprehensiveStatsByGeoGroup || [];
172
+ // 부모에게 지역별 KPI 통계 전달
173
+ this.dispatchEvent(new CustomEvent('regional-kpi-stats-loaded', {
174
+ detail: { data: this.regionalKpiStats },
175
+ bubbles: true,
176
+ composed: true
177
+ }));
178
+ }
179
+ catch (error) {
180
+ console.error('Failed to fetch regional KPI stats:', error);
181
+ this.regionalKpiStats = [];
182
+ }
183
+ }
184
+ async fetchMonthlyTrendData() {
185
+ try {
186
+ // selectedCategory에 따라 kpiName 결정
187
+ const categoryToKpiName = {
188
+ '전체 KPI': null,
189
+ '일정 성과': 'Y1. 일정성과',
190
+ '비용 성과': 'Y2. 비용성과',
191
+ '품질 성과': 'Y3. 품질성과',
192
+ '안전 성과': 'Y4. 안전성과',
193
+ '환경 성과': 'Y5. 환경성과',
194
+ '생산성 성과': 'Y6. 생산성성과'
195
+ };
196
+ const kpiName = categoryToKpiName[this.selectedCategory];
197
+ const response = await client.query({
198
+ query: gql `
199
+ query GetMonthlyTrendData(
200
+ $kpiName: String
201
+ $startYearMonth: String
202
+ $endYearMonth: String
203
+ $sectorType: String
204
+ $buildingUsage: String
205
+ ) {
206
+ kpiZValueMonthlyTrendByGeoGroup(
207
+ kpiName: $kpiName
208
+ startYearMonth: $startYearMonth
209
+ endYearMonth: $endYearMonth
210
+ sectorType: $sectorType
211
+ buildingUsage: $buildingUsage
212
+ ) {
213
+ geoGroup
214
+ yearMonth
215
+ avgVal
216
+ projectCount
217
+ }
218
+ }
219
+ `,
220
+ variables: {
221
+ kpiName,
222
+ startYearMonth: this.startYearMonth,
223
+ endYearMonth: this.endYearMonth,
224
+ sectorType: this.sectorType || null,
225
+ buildingUsage: this.buildingUsage || null
226
+ }
227
+ });
228
+ this.monthlyTrendData = response.data.kpiZValueMonthlyTrendByGeoGroup || [];
229
+ // 부모에게 월별 추이 데이터 전달
230
+ this.dispatchEvent(new CustomEvent('monthly-trend-data-loaded', {
231
+ detail: { data: this.monthlyTrendData },
232
+ bubbles: true,
233
+ composed: true
234
+ }));
235
+ }
236
+ catch (error) {
237
+ console.error('Failed to fetch monthly trend data:', error);
238
+ this.monthlyTrendData = [];
239
+ }
240
+ }
241
+ getRegionTrendData(region) {
242
+ // 해당 지역의 최근 12개월 트렌드 데이터 추출
243
+ const regionData = this.monthlyTrendData
244
+ .filter(d => d.geoGroup === region)
245
+ .sort((a, b) => a.yearMonth.localeCompare(b.yearMonth))
246
+ .map(d => d.avgVal * 100); // 20배 스케일
247
+ // 12개월 데이터가 없으면 빈 배열 반환
248
+ return regionData.length > 0 ? regionData : [];
249
+ }
250
+ calculateChangeRate(region) {
251
+ // 해당 지역의 월별 데이터를 시간순으로 정렬
252
+ const regionData = this.monthlyTrendData
253
+ .filter(d => d.geoGroup === region)
254
+ .sort((a, b) => a.yearMonth.localeCompare(b.yearMonth));
255
+ if (regionData.length < 2)
256
+ return 0;
257
+ // 가장 오래된 달과 가장 최근 달의 값 비교
258
+ const oldestValue = regionData[0].avgVal;
259
+ const latestValue = regionData[regionData.length - 1].avgVal;
260
+ if (oldestValue === 0)
261
+ return 0;
262
+ // 변동율 계산: (최근값 - 과거값) / 과거값 * 100
263
+ return ((latestValue - oldestValue) / oldestValue) * 100;
264
+ }
265
+ generateChartData() {
266
+ // KPI 이름을 카테고리로 매핑 및 축약
267
+ const categoryMapping = {
268
+ 'Y1. 일정성과': '일정',
269
+ 'Y2. 비용성과': '비용',
270
+ 'Y3. 품질성과': '품질',
271
+ 'Y4. 안전성과': '안전',
272
+ 'Y5. 환경성과': '환경',
273
+ 'Y6. 생산성성과': '생산성'
274
+ };
275
+ if (this.selectedChartType === 'radar') {
276
+ // 레이더 차트용 데이터 생성 (전체 평균만 표시)
277
+ const data = [];
278
+ const categories = [];
279
+ this.kpiYComprehensiveStats.forEach(stat => {
280
+ const category = categoryMapping[stat.kpiName] || stat.kpiName;
281
+ if (!categories.includes(category)) {
282
+ categories.push(category);
283
+ }
284
+ // 전체 평균 데이터만 표시 (비교 시리즈 없음)
285
+ data.push({
286
+ org: '전체평균',
287
+ category,
288
+ value: Math.round(stat.avgVal * 100)
289
+ });
290
+ });
291
+ this.chartCategories = categories.length > 0 ? categories : ['일정', '비용', '품질', '안전', '환경', '생산성'];
292
+ this.chartData = data;
293
+ }
294
+ else {
295
+ // 박스플롯용 데이터 생성 (sv-project-detail.ts의 getYKpiBoxplotData 참조)
296
+ const data = [];
297
+ const categories = [];
298
+ this.kpiYComprehensiveStats.forEach(stat => {
299
+ const category = categoryMapping[stat.kpiName] || stat.kpiName;
300
+ if (!categories.includes(category)) {
301
+ categories.push(category);
302
+ }
303
+ data.push({
304
+ org: category,
305
+ min: stat.minVal * 100,
306
+ max: stat.maxVal * 100,
307
+ q1: stat.q1Val * 100,
308
+ q3: stat.q3Val * 100,
309
+ median: stat.medVal * 100,
310
+ mean: stat.avgVal * 100,
311
+ value: stat.avgVal * 100
312
+ });
313
+ });
314
+ this.chartCategories = categories.length > 0 ? categories : ['일정', '비용', '품질', '안전', '환경', '생산성'];
315
+ this.chartData = data;
316
+ }
317
+ }
318
+ _onAllPeriodChange(e) {
319
+ this.isAllPeriod = e.target.checked;
320
+ if (this.isAllPeriod) {
321
+ this.startYearMonth = '';
322
+ this.endYearMonth = '';
323
+ }
324
+ }
325
+ onCategoryChange(event) {
326
+ const target = event.target;
327
+ this.selectedCategory = target.value;
328
+ // 카테고리 변경 시 지역별 통계와 월별 추이 데이터만 다시 가져오기
329
+ // 종합 성과(박스플롯/레이더)는 항상 전체 Y-level KPI를 표시
330
+ this.fetchRegionalKpiStats();
331
+ this.fetchMonthlyTrendData();
332
+ this.dispatchEvent(new CustomEvent('category-change', {
333
+ detail: { category: target.value },
334
+ bubbles: true,
335
+ composed: true
336
+ }));
337
+ }
338
+ onChartTypeChange(type) {
339
+ this.selectedChartType = type;
340
+ this.generateChartData();
341
+ }
342
+ onRegionClick(region) {
343
+ // 지도를 해당 광역시도로 포커스
344
+ this.dispatchEvent(new CustomEvent('focus-region', {
345
+ detail: { region },
346
+ bubbles: true,
347
+ composed: true
348
+ }));
349
+ // 팝업 열기
350
+ this.dispatchEvent(new CustomEvent('region-click', {
351
+ detail: { region },
352
+ bubbles: true,
353
+ composed: true
354
+ }));
355
+ }
356
+ downloadExcel() {
357
+ import('xlsx').then(XLSX => {
358
+ const filterLabel = [
359
+ this.selectedCategory,
360
+ this.sectorType === 'PUBLIC' ? '공공' : this.sectorType === 'PRIVATE' ? '민간' : '',
361
+ this.buildingUsage === 'RESIDENTIAL' ? '주거' : this.buildingUsage === 'NON_RESIDENTIAL' ? '비주거' : ''
362
+ ]
363
+ .filter(Boolean)
364
+ .join('_');
365
+ const period = this.isAllPeriod ? '전체기간' : `${this.startYearMonth}~${this.endYearMonth}`;
366
+ const wb = XLSX.utils.book_new();
367
+ // Sheet 1: 검색 조건
368
+ const condData = [
369
+ { 항목: 'KPI 카테고리', 값: this.selectedCategory },
370
+ { 항목: '사업 유형', 값: this.sectorType === 'PUBLIC' ? '공공' : this.sectorType === 'PRIVATE' ? '민간' : '전체' },
371
+ { 항목: '건물 용도', 값: this.buildingUsage === 'RESIDENTIAL' ? '주거' : this.buildingUsage === 'NON_RESIDENTIAL' ? '비주거' : '전체' },
372
+ { 항목: '기간', 값: this.isAllPeriod ? '전체 기간' : `${this.startYearMonth} ~ ${this.endYearMonth}` },
373
+ { 항목: '다운로드 일시', 값: new Date().toLocaleString('ko-KR') }
374
+ ];
375
+ const wsCond = XLSX.utils.json_to_sheet(condData);
376
+ wsCond['!cols'] = [{ wch: 16 }, { wch: 30 }];
377
+ XLSX.utils.book_append_sheet(wb, wsCond, '검색 조건');
378
+ // Sheet 2: 종합 성과
379
+ const yData = this.kpiYComprehensiveStats.map((s) => ({
380
+ KPI: s.kpiName,
381
+ 최소: Number((s.minVal * 100).toFixed(2)),
382
+ Q1: Number((s.q1Val * 100).toFixed(2)),
383
+ 중앙값: Number((s.medVal * 100).toFixed(2)),
384
+ Q3: Number((s.q3Val * 100).toFixed(2)),
385
+ 최대: Number((s.maxVal * 100).toFixed(2)),
386
+ 평균: Number((s.avgVal * 100).toFixed(2))
387
+ }));
388
+ const ws1 = XLSX.utils.json_to_sheet(yData);
389
+ ws1['!cols'] = [{ wch: 20 }, { wch: 10 }, { wch: 10 }, { wch: 10 }, { wch: 10 }, { wch: 10 }, { wch: 10 }];
390
+ XLSX.utils.book_append_sheet(wb, ws1, '종합 성과');
391
+ // Sheet 2: 시도별 성과
392
+ const rData = this.getSortedRegionData().map((item) => {
393
+ var _a;
394
+ const stat = this.regionalKpiStats.find((s) => s.geoGroup === item.region);
395
+ return {
396
+ 지역: item.region,
397
+ 'KPI 점수': Number(item.kpi) || 0,
398
+ '변동률(%)': Number(Number(item.change).toFixed(2)),
399
+ 프로젝트수: (_a = stat === null || stat === void 0 ? void 0 : stat.projectCount) !== null && _a !== void 0 ? _a : 0
400
+ };
401
+ });
402
+ const ws2 = XLSX.utils.json_to_sheet(rData);
403
+ ws2['!cols'] = [{ wch: 16 }, { wch: 12 }, { wch: 12 }, { wch: 12 }];
404
+ XLSX.utils.book_append_sheet(wb, ws2, '시도별 성과');
405
+ // Sheet 3: 월별 추이
406
+ if (this.monthlyTrendData.length > 0) {
407
+ const mData = this.monthlyTrendData.map((d) => ({
408
+ 지역: d.geoGroup,
409
+ 기간: d.yearMonth,
410
+ 평균: Number((d.avgVal * 100).toFixed(2)),
411
+ 프로젝트수: d.projectCount
412
+ }));
413
+ const ws3 = XLSX.utils.json_to_sheet(mData);
414
+ ws3['!cols'] = [{ wch: 16 }, { wch: 12 }, { wch: 10 }, { wch: 12 }];
415
+ XLSX.utils.book_append_sheet(wb, ws3, '월별 추이');
416
+ }
417
+ XLSX.writeFile(wb, `KPI_대시보드_${filterLabel}_${period}.xlsx`);
418
+ });
419
+ }
420
+ // regionOrder에 따라 지역 데이터를 정렬
421
+ getSortedRegionData() {
422
+ // 실제 데이터를 기반으로 각 시도에 대한 데이터 생성
423
+ return this.regionOrder.map(regionName => {
424
+ const stat = this.regionalKpiStats.find(s => s.geoGroup === regionName);
425
+ const changeRate = this.calculateChangeRate(regionName);
426
+ return {
427
+ region: regionName,
428
+ kpi: stat ? (stat.avgVal * 100).toFixed(1) : '-',
429
+ change: changeRate.toFixed(2),
430
+ trendData: this.getRegionTrendData(regionName),
431
+ lat: 36.5,
432
+ lng: 127.5
433
+ };
434
+ });
435
+ }
436
+ getChangeRateClass(change) {
437
+ if (change > 0)
438
+ return 'change-up';
439
+ if (change < 0)
440
+ return 'change-down';
441
+ return 'change-neutral';
442
+ }
443
+ getChangeIcon(change) {
444
+ if (change > 0)
445
+ return '▲';
446
+ if (change < 0)
447
+ return '▼';
448
+ return '─';
449
+ }
450
+ render() {
451
+ return html `
452
+ <div class="panel-header">
453
+ <div class="panel-title">전국 KPI</div>
454
+ <button class="panel-close" style="visibility: hidden;">×</button>
455
+ </div>
456
+ <div class="panel-content">
457
+ <!-- 필터: KPI / 사업유형 / 건물용도 -->
458
+ <div class="filter-row">
459
+ <span class="filter-label">KPI</span>
460
+ <select class="filter-select" .value=${this.selectedCategory} @change=${this.onCategoryChange}>
461
+ <option value="전체 KPI">전체 KPI</option>
462
+ <option value="일정 성과">일정</option>
463
+ <option value="비용 성과">비용</option>
464
+ <option value="품질 성과">품질</option>
465
+ <option value="안전 성과">안전</option>
466
+ <option value="환경 성과">환경</option>
467
+ <option value="생산성 성과">생산성</option>
468
+ </select>
469
+ <span class="filter-label">유형</span>
470
+ <select
471
+ class="filter-select"
472
+ .value=${this.sectorType}
473
+ @change=${(e) => {
474
+ this.sectorType = e.target.value;
475
+ }}
476
+ >
477
+ <option value="">전체</option>
478
+ <option value="PUBLIC">공공</option>
479
+ <option value="PRIVATE">민간</option>
480
+ </select>
481
+ <span class="filter-label">용도</span>
482
+ <select
483
+ class="filter-select"
484
+ .value=${this.buildingUsage}
485
+ @change=${(e) => {
486
+ this.buildingUsage = e.target.value;
487
+ }}
488
+ >
489
+ <option value="">전체</option>
490
+ <option value="RESIDENTIAL">주거</option>
491
+ <option value="NON_RESIDENTIAL">비주거</option>
492
+ </select>
493
+ </div>
494
+
495
+ <!-- 기간 필터 -->
496
+ <div class="filter-row">
497
+ <span class="filter-label">기간</span>
498
+ <select class="filter-select" ?disabled=${this.isAllPeriod} @change=${(e) => {
499
+ const y = e.target.value;
500
+ const m = this.startYearMonth.split('-')[1] || '01';
501
+ this.startYearMonth = `${y}-${m}`;
502
+ }}>
503
+ ${[2022, 2023, 2024, 2025, 2026].map(y => html `<option value="${y}" ?selected=${this.startYearMonth.startsWith(String(y))}>${y}</option>`)}
504
+ </select>
505
+ <select class="filter-select" ?disabled=${this.isAllPeriod} @change=${(e) => {
506
+ const y = this.startYearMonth.split('-')[0] || '2025';
507
+ const m = e.target.value.padStart(2, '0');
508
+ this.startYearMonth = `${y}-${m}`;
509
+ }}>
510
+ ${[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].map(m => html `<option value="${m}" ?selected=${parseInt(this.startYearMonth.split('-')[1]) === m}>${m}월</option>`)}
511
+ </select>
512
+ <span style="color:#6c757d;font-size:0.8rem;">~</span>
513
+ <select class="filter-select" ?disabled=${this.isAllPeriod} @change=${(e) => {
514
+ const y = e.target.value;
515
+ const m = this.endYearMonth.split('-')[1] || '12';
516
+ this.endYearMonth = `${y}-${m}`;
517
+ }}>
518
+ ${[2022, 2023, 2024, 2025, 2026].map(y => html `<option value="${y}" ?selected=${this.endYearMonth.startsWith(String(y))}>${y}</option>`)}
519
+ </select>
520
+ <select class="filter-select" ?disabled=${this.isAllPeriod} @change=${(e) => {
521
+ const y = this.endYearMonth.split('-')[0] || '2026';
522
+ const m = e.target.value.padStart(2, '0');
523
+ this.endYearMonth = `${y}-${m}`;
524
+ }}>
525
+ ${[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].map(m => html `<option value="${m}" ?selected=${parseInt(this.endYearMonth.split('-')[1]) === m}>${m}월</option>`)}
526
+ </select>
527
+ <label style="display:flex;align-items:center;gap:3px;cursor:pointer;">
528
+ <input type="checkbox" .checked=${this.isAllPeriod} @change=${this._onAllPeriodChange} style="cursor:pointer;" />
529
+ <span class="filter-label" style="min-width:auto;">전체</span>
530
+ </label>
531
+ </div>
532
+
533
+ <!-- 종합 성과 -->
534
+ <div class="chart-section">
535
+ <div class="sub-title">종합 성과</div>
536
+ <div class="chart-toggle">
537
+ <button
538
+ class="toggle-button ${this.selectedChartType === 'boxplot' ? 'active' : ''}"
539
+ @click=${() => this.onChartTypeChange('boxplot')}
540
+ >
541
+ 박스플롯
542
+ </button>
543
+ <button
544
+ class="toggle-button ${this.selectedChartType === 'radar' ? 'active' : ''}"
545
+ @click=${() => this.onChartTypeChange('radar')}
546
+ >
547
+ 레이더차트
548
+ </button>
549
+ </div>
550
+ <div class="chart-container">
551
+ ${this.selectedChartType === 'boxplot'
552
+ ? html ` <sv-kpi-boxplot-chart .data=${this.chartData} .valueKey=${'value'}></sv-kpi-boxplot-chart> `
553
+ : html `
554
+ <sv-kpi-radar-chart
555
+ .data=${this.chartData}
556
+ .categories=${this.chartCategories}
557
+ .valueKey=${'value'}
558
+ ></sv-kpi-radar-chart>
559
+ `}
560
+ </div>
561
+ </div>
562
+
563
+ <!-- 시도별 성과 -->
564
+ <div class="chart-section">
565
+ <div class="sub-title">시도별 성과</div>
566
+ <table class="performance-table">
567
+ <thead>
568
+ <tr>
569
+ <th>지역명</th>
570
+ <th>KPI</th>
571
+ <th>변동률(%)</th>
572
+ <th>성과 추이</th>
573
+ </tr>
574
+ </thead>
575
+ <tbody>
576
+ ${this.getSortedRegionData().map((item) => html `
577
+ <tr
578
+ style="cursor: pointer; transition: background-color 0.2s;"
579
+ @click=${() => this.onRegionClick(item.region)}
580
+ @mouseenter=${(e) => (e.target.style.backgroundColor = '#f8f9fa')}
581
+ @mouseleave=${(e) => (e.target.style.backgroundColor = '')}
582
+ >
583
+ <td>${item.region}</td>
584
+ <td>${item.kpi}</td>
585
+ <td>
586
+ <div class="change-rate ${this.getChangeRateClass(item.change)}">
587
+ ${this.getChangeIcon(item.change)}${Math.abs(item.change)}%
588
+ </div>
589
+ </td>
590
+ <td>
591
+ ${item.trendData && item.trendData.length > 0
592
+ ? html `
593
+ <sv-kpi-mini-trend-chart
594
+ .data=${item.trendData}
595
+ .width=${60}
596
+ .height=${30}
597
+ .lineColor=${'#2196f3'}
598
+ .strokeWidth=${1.5}
599
+ .showPoints=${true}
600
+ .pointRadius=${1.5}
601
+ ></sv-kpi-mini-trend-chart>
602
+ `
603
+ : html `<span style="color: #999;">-</span>`}
604
+ </td>
605
+ </tr>
606
+ `)}
607
+ </tbody>
608
+ </table>
609
+ <button class="download-button" @click=${this.downloadExcel}>📊 엑셀 다운로드</button>
610
+ </div>
611
+ </div>
612
+ `;
613
+ }
614
+ };
615
+ KpiLeftPanel.styles = [
616
+ ScrollbarStyles,
617
+ css `
618
+ :host {
619
+ display: block;
620
+ width: 400px;
621
+ background: #fff;
622
+ border-right: 1px solid #e0e0e0;
623
+ overflow: hidden;
624
+ display: flex;
625
+ flex-direction: column;
626
+ box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
627
+ }
628
+ .panel-content {
629
+ padding: 20px;
630
+ overflow-y: auto;
631
+ flex: 1;
632
+ }
633
+ .panel-header {
634
+ display: flex;
635
+ justify-content: space-between;
636
+ align-items: center;
637
+ padding: 20px;
638
+ border-bottom: 1px solid #e0e0e0;
639
+ background: #fff;
640
+ height: 70px;
641
+ box-sizing: border-box;
642
+ }
643
+ .panel-title {
644
+ font-size: 1.3rem;
645
+ font-weight: bold;
646
+ color: #333;
647
+ margin: 0;
648
+ }
649
+ .panel-close {
650
+ width: 32px;
651
+ height: 32px;
652
+ border: none;
653
+ background: #fff;
654
+ border-radius: 50%;
655
+ cursor: pointer;
656
+ display: flex;
657
+ align-items: center;
658
+ justify-content: center;
659
+ font-size: 1.2rem;
660
+ color: #666;
661
+ transition: all 0.2s;
662
+ }
663
+ .panel-close:hover {
664
+ background: #e9ecef;
665
+ color: #333;
666
+ }
667
+ .sub-title {
668
+ font-size: 1rem;
669
+ font-weight: 600;
670
+ margin-bottom: 16px;
671
+ color: #495057;
672
+ }
673
+ .chart-section {
674
+ background: #f8f9fa;
675
+ border-radius: 8px;
676
+ padding: 16px;
677
+ margin-bottom: 20px;
678
+ }
679
+ .chart-toggle {
680
+ display: flex;
681
+ gap: 8px;
682
+ margin-bottom: 16px;
683
+ }
684
+ .toggle-button {
685
+ padding: 8px 16px;
686
+ border: 1px solid #ced4da;
687
+ background: #fff;
688
+ border-radius: 6px;
689
+ cursor: pointer;
690
+ font-size: 0.9rem;
691
+ transition: all 0.2s;
692
+ }
693
+ .toggle-button.active {
694
+ background: #667eea;
695
+ color: white;
696
+ border-color: #667eea;
697
+ }
698
+ .chart-container {
699
+ height: 300px;
700
+ display: flex;
701
+ align-items: center;
702
+ justify-content: center;
703
+ background: white;
704
+ border-radius: 6px;
705
+ border: 1px solid #e9ecef;
706
+ }
707
+ .performance-table {
708
+ width: 100%;
709
+ border-collapse: collapse;
710
+ margin-top: 16px;
711
+ }
712
+ .performance-table th,
713
+ .performance-table td {
714
+ padding: 12px 8px;
715
+ text-align: left;
716
+ border-bottom: 1px solid #e9ecef;
717
+ }
718
+ .performance-table th {
719
+ background: #f8f9fa;
720
+ font-weight: 600;
721
+ color: #495057;
722
+ }
723
+ .performance-table td {
724
+ color: #333;
725
+ }
726
+ .change-rate {
727
+ display: flex;
728
+ align-items: center;
729
+ gap: 4px;
730
+ }
731
+ .change-up {
732
+ color: #dc3545;
733
+ }
734
+ .change-down {
735
+ color: #198754;
736
+ }
737
+ .change-neutral {
738
+ color: #6c757d;
739
+ }
740
+ .trend-chart {
741
+ width: 60px;
742
+ height: 30px;
743
+ background: #f8f9fa;
744
+ border-radius: 4px;
745
+ display: flex;
746
+ align-items: center;
747
+ justify-content: center;
748
+ font-size: 0.8rem;
749
+ color: #666;
750
+ }
751
+ .download-button {
752
+ margin-top: 16px;
753
+ padding: 8px 16px;
754
+ background: #28a745;
755
+ color: white;
756
+ border: none;
757
+ border-radius: 6px;
758
+ cursor: pointer;
759
+ font-size: 0.9rem;
760
+ display: flex;
761
+ align-items: center;
762
+ gap: 8px;
763
+ }
764
+ .download-button:hover {
765
+ background: #218838;
766
+ }
767
+ .filter-row {
768
+ display: flex;
769
+ align-items: center;
770
+ justify-content: space-between;
771
+ gap: 6px;
772
+ margin-bottom: 10px;
773
+ }
774
+ .filter-label {
775
+ font-size: 0.75rem;
776
+ font-weight: 600;
777
+ color: #495057;
778
+ white-space: nowrap;
779
+ }
780
+ .filter-select {
781
+ flex: 1;
782
+ min-width: 0;
783
+ padding: 4px 6px;
784
+ border: 1px solid #ced4da;
785
+ border-radius: 4px;
786
+ background: white;
787
+ font-size: 0.78rem;
788
+ }
789
+ .filter-select:focus {
790
+ outline: none;
791
+ border-color: #667eea;
792
+ }
793
+ `
794
+ ];
795
+ __decorate([
796
+ property({ type: String }),
797
+ __metadata("design:type", Object)
798
+ ], KpiLeftPanel.prototype, "selectedCategory", void 0);
799
+ __decorate([
800
+ property({ type: String }),
801
+ __metadata("design:type", Object)
802
+ ], KpiLeftPanel.prototype, "selectedChartType", void 0);
803
+ __decorate([
804
+ property({ type: Array }),
805
+ __metadata("design:type", Array)
806
+ ], KpiLeftPanel.prototype, "mapData", void 0);
807
+ __decorate([
808
+ property({ type: String }),
809
+ __metadata("design:type", Object)
810
+ ], KpiLeftPanel.prototype, "startYearMonth", void 0);
811
+ __decorate([
812
+ property({ type: String }),
813
+ __metadata("design:type", Object)
814
+ ], KpiLeftPanel.prototype, "endYearMonth", void 0);
815
+ __decorate([
816
+ property({ type: String }),
817
+ __metadata("design:type", Object)
818
+ ], KpiLeftPanel.prototype, "sectorType", void 0);
819
+ __decorate([
820
+ property({ type: String }),
821
+ __metadata("design:type", Object)
822
+ ], KpiLeftPanel.prototype, "buildingUsage", void 0);
823
+ __decorate([
824
+ state(),
825
+ __metadata("design:type", Object)
826
+ ], KpiLeftPanel.prototype, "isAllPeriod", void 0);
827
+ __decorate([
828
+ state(),
829
+ __metadata("design:type", Array)
830
+ ], KpiLeftPanel.prototype, "chartData", void 0);
831
+ __decorate([
832
+ state(),
833
+ __metadata("design:type", Array)
834
+ ], KpiLeftPanel.prototype, "chartCategories", void 0);
835
+ __decorate([
836
+ state(),
837
+ __metadata("design:type", Array)
838
+ ], KpiLeftPanel.prototype, "kpiYComprehensiveStats", void 0);
839
+ __decorate([
840
+ state(),
841
+ __metadata("design:type", Array)
842
+ ], KpiLeftPanel.prototype, "regionalKpiStats", void 0);
843
+ __decorate([
844
+ state(),
845
+ __metadata("design:type", Array)
846
+ ], KpiLeftPanel.prototype, "monthlyTrendData", void 0);
847
+ KpiLeftPanel = __decorate([
848
+ customElement('kpi-left-panel')
849
+ ], KpiLeftPanel);
850
+ export { KpiLeftPanel };
851
+ //# sourceMappingURL=kpi-left-panel.js.map