@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,642 @@
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.chartData = [];
19
+ this.chartCategories = [];
20
+ this.kpiYComprehensiveStats = [];
21
+ this.regionalKpiStats = [];
22
+ this.monthlyTrendData = [];
23
+ // 행정안전부 행정구역코드 순서
24
+ this.regionOrder = [
25
+ '서울특별시',
26
+ '부산광역시',
27
+ '대구광역시',
28
+ '인천광역시',
29
+ '광주광역시',
30
+ '대전광역시',
31
+ '울산광역시',
32
+ '세종특별자치시',
33
+ '경기도',
34
+ '강원도',
35
+ '충청북도',
36
+ '충청남도',
37
+ '전라북도',
38
+ '전라남도',
39
+ '경상북도',
40
+ '경상남도',
41
+ '제주특별자치도'
42
+ ];
43
+ }
44
+ connectedCallback() {
45
+ super.connectedCallback();
46
+ this.fetchKpiYComprehensiveStats();
47
+ this.fetchRegionalKpiStats();
48
+ this.fetchMonthlyTrendData();
49
+ }
50
+ updated(changedProperties) {
51
+ // selectedCategory, startYearMonth, endYearMonth가 변경되면 데이터 다시 가져오기
52
+ if (changedProperties.has('selectedCategory') ||
53
+ changedProperties.has('startYearMonth') ||
54
+ changedProperties.has('endYearMonth')) {
55
+ // 기간 정보가 설정되어 있을 때만 조회 (빈 문자열이면 전체 기간)
56
+ if (this.startYearMonth !== undefined && this.endYearMonth !== undefined) {
57
+ this.fetchKpiYComprehensiveStats();
58
+ this.fetchRegionalKpiStats();
59
+ this.fetchMonthlyTrendData();
60
+ }
61
+ }
62
+ }
63
+ async fetchKpiYComprehensiveStats() {
64
+ try {
65
+ const response = await client.query({
66
+ query: gql `
67
+ query GetKpiYValueComprehensiveStats($startYearMonth: String, $endYearMonth: String) {
68
+ totalKpiYValueComprehensiveStats(startYearMonth: $startYearMonth, endYearMonth: $endYearMonth) {
69
+ kpiName
70
+ minVal
71
+ q1Val
72
+ medVal
73
+ q3Val
74
+ maxVal
75
+ avgVal
76
+ }
77
+ }
78
+ `,
79
+ variables: {
80
+ startYearMonth: this.startYearMonth,
81
+ endYearMonth: this.endYearMonth
82
+ }
83
+ });
84
+ this.kpiYComprehensiveStats = response.data.totalKpiYValueComprehensiveStats || [];
85
+ console.log('KPI Y-Value Comprehensive Stats:', this.kpiYComprehensiveStats);
86
+ // 부모에게 전체 Y-level KPI 통계 전달
87
+ this.dispatchEvent(new CustomEvent('total-kpi-y-stats-loaded', {
88
+ detail: { data: this.kpiYComprehensiveStats },
89
+ bubbles: true,
90
+ composed: true
91
+ }));
92
+ this.generateChartData();
93
+ }
94
+ catch (error) {
95
+ console.error('Failed to fetch KPI Y comprehensive stats:', error);
96
+ this.kpiYComprehensiveStats = [];
97
+ this.generateChartData();
98
+ }
99
+ }
100
+ async fetchRegionalKpiStats() {
101
+ try {
102
+ // selectedCategory에 따라 kpiName 결정
103
+ const categoryToKpiName = {
104
+ '전체 KPI': null,
105
+ '일정 성과': 'Y1. 일정성과',
106
+ '비용 성과': 'Y2. 비용성과',
107
+ '품질 성과': 'Y3. 품질성과',
108
+ '안전 성과': 'Y4. 안전성과',
109
+ '환경 성과': 'Y5. 환경성과',
110
+ '생산성 성과': 'Y6. 생산성성과'
111
+ };
112
+ const kpiName = categoryToKpiName[this.selectedCategory];
113
+ const response = await client.query({
114
+ query: gql `
115
+ query GetRegionalKpiStats($kpiName: String, $startYearMonth: String, $endYearMonth: String) {
116
+ kpiZValueComprehensiveStatsByGeoGroup(
117
+ kpiName: $kpiName
118
+ startYearMonth: $startYearMonth
119
+ endYearMonth: $endYearMonth
120
+ ) {
121
+ geoGroup
122
+ avgVal
123
+ minVal
124
+ maxVal
125
+ projectCount
126
+ }
127
+ }
128
+ `,
129
+ variables: {
130
+ kpiName,
131
+ startYearMonth: this.startYearMonth,
132
+ endYearMonth: this.endYearMonth
133
+ }
134
+ });
135
+ this.regionalKpiStats = response.data.kpiZValueComprehensiveStatsByGeoGroup || [];
136
+ console.log('Regional KPI Stats:', this.regionalKpiStats);
137
+ // 부모에게 지역별 KPI 통계 전달
138
+ this.dispatchEvent(new CustomEvent('regional-kpi-stats-loaded', {
139
+ detail: { data: this.regionalKpiStats },
140
+ bubbles: true,
141
+ composed: true
142
+ }));
143
+ }
144
+ catch (error) {
145
+ console.error('Failed to fetch regional KPI stats:', error);
146
+ this.regionalKpiStats = [];
147
+ }
148
+ }
149
+ async fetchMonthlyTrendData() {
150
+ try {
151
+ // selectedCategory에 따라 kpiName 결정
152
+ const categoryToKpiName = {
153
+ '전체 KPI': null,
154
+ '일정 성과': 'Y1. 일정성과',
155
+ '비용 성과': 'Y2. 비용성과',
156
+ '품질 성과': 'Y3. 품질성과',
157
+ '안전 성과': 'Y4. 안전성과',
158
+ '환경 성과': 'Y5. 환경성과',
159
+ '생산성 성과': 'Y6. 생산성성과'
160
+ };
161
+ const kpiName = categoryToKpiName[this.selectedCategory];
162
+ const response = await client.query({
163
+ query: gql `
164
+ query GetMonthlyTrendData($kpiName: String, $startYearMonth: String, $endYearMonth: String) {
165
+ kpiZValueMonthlyTrendByGeoGroup(kpiName: $kpiName, startYearMonth: $startYearMonth, endYearMonth: $endYearMonth) {
166
+ geoGroup
167
+ yearMonth
168
+ avgVal
169
+ projectCount
170
+ }
171
+ }
172
+ `,
173
+ variables: {
174
+ kpiName,
175
+ startYearMonth: this.startYearMonth,
176
+ endYearMonth: this.endYearMonth
177
+ }
178
+ });
179
+ this.monthlyTrendData = response.data.kpiZValueMonthlyTrendByGeoGroup || [];
180
+ console.log('Monthly Trend Data:', this.monthlyTrendData);
181
+ // 부모에게 월별 추이 데이터 전달
182
+ this.dispatchEvent(new CustomEvent('monthly-trend-data-loaded', {
183
+ detail: { data: this.monthlyTrendData },
184
+ bubbles: true,
185
+ composed: true
186
+ }));
187
+ }
188
+ catch (error) {
189
+ console.error('Failed to fetch monthly trend data:', error);
190
+ this.monthlyTrendData = [];
191
+ }
192
+ }
193
+ getRegionTrendData(region) {
194
+ // 해당 지역의 최근 12개월 트렌드 데이터 추출
195
+ const regionData = this.monthlyTrendData
196
+ .filter(d => d.geoGroup === region)
197
+ .sort((a, b) => a.yearMonth.localeCompare(b.yearMonth))
198
+ .map(d => d.avgVal * 20); // 20배 스케일
199
+ // 12개월 데이터가 없으면 빈 배열 반환
200
+ return regionData.length > 0 ? regionData : [];
201
+ }
202
+ calculateChangeRate(region) {
203
+ // 해당 지역의 월별 데이터를 시간순으로 정렬
204
+ const regionData = this.monthlyTrendData
205
+ .filter(d => d.geoGroup === region)
206
+ .sort((a, b) => a.yearMonth.localeCompare(b.yearMonth));
207
+ if (regionData.length < 2)
208
+ return 0;
209
+ // 가장 오래된 달과 가장 최근 달의 값 비교
210
+ const oldestValue = regionData[0].avgVal;
211
+ const latestValue = regionData[regionData.length - 1].avgVal;
212
+ if (oldestValue === 0)
213
+ return 0;
214
+ // 변동율 계산: (최근값 - 과거값) / 과거값 * 100
215
+ return ((latestValue - oldestValue) / oldestValue) * 100;
216
+ }
217
+ generateChartData() {
218
+ // KPI 이름을 카테고리로 매핑 및 축약
219
+ const categoryMapping = {
220
+ 'Y1. 일정성과': '일정',
221
+ 'Y2. 비용성과': '비용',
222
+ 'Y3. 품질성과': '품질',
223
+ 'Y4. 안전성과': '안전',
224
+ 'Y5. 환경성과': '환경',
225
+ 'Y6. 생산성성과': '생산성'
226
+ };
227
+ console.log('generateChartData - selectedChartType:', this.selectedChartType);
228
+ console.log('generateChartData - kpiYComprehensiveStats:', this.kpiYComprehensiveStats);
229
+ if (this.selectedChartType === 'radar') {
230
+ // 레이더 차트용 데이터 생성 (전체 평균만 표시)
231
+ const data = [];
232
+ const categories = [];
233
+ this.kpiYComprehensiveStats.forEach(stat => {
234
+ const category = categoryMapping[stat.kpiName] || stat.kpiName;
235
+ if (!categories.includes(category)) {
236
+ categories.push(category);
237
+ }
238
+ // 전체 평균 데이터만 표시 (비교 시리즈 없음)
239
+ data.push({
240
+ org: '전체평균',
241
+ category,
242
+ value: Math.round(stat.avgVal * 20)
243
+ });
244
+ });
245
+ this.chartCategories = categories.length > 0 ? categories : ['일정', '비용', '품질', '안전', '환경', '생산성'];
246
+ this.chartData = data;
247
+ console.log('Radar chart data:', data);
248
+ console.log('Radar chart categories:', this.chartCategories);
249
+ }
250
+ else {
251
+ // 박스플롯용 데이터 생성 (sv-project-detail.ts의 getYKpiBoxplotData 참조)
252
+ const data = [];
253
+ const categories = [];
254
+ this.kpiYComprehensiveStats.forEach(stat => {
255
+ const category = categoryMapping[stat.kpiName] || stat.kpiName;
256
+ if (!categories.includes(category)) {
257
+ categories.push(category);
258
+ }
259
+ data.push({
260
+ org: category,
261
+ min: stat.minVal * 20,
262
+ max: stat.maxVal * 20,
263
+ q1: stat.q1Val * 20,
264
+ q3: stat.q3Val * 20,
265
+ median: stat.medVal * 20,
266
+ mean: stat.avgVal * 20,
267
+ value: stat.avgVal * 20
268
+ });
269
+ });
270
+ this.chartCategories = categories.length > 0 ? categories : ['일정', '비용', '품질', '안전', '환경', '생산성'];
271
+ this.chartData = data;
272
+ console.log('Boxplot chart data:', data);
273
+ console.log('Boxplot chart categories:', this.chartCategories);
274
+ }
275
+ }
276
+ onCategoryChange(event) {
277
+ const target = event.target;
278
+ this.selectedCategory = target.value;
279
+ // 카테고리 변경 시 지역별 통계와 월별 추이 데이터만 다시 가져오기
280
+ // 종합 성과(박스플롯/레이더)는 항상 전체 Y-level KPI를 표시
281
+ this.fetchRegionalKpiStats();
282
+ this.fetchMonthlyTrendData();
283
+ this.dispatchEvent(new CustomEvent('category-change', {
284
+ detail: { category: target.value },
285
+ bubbles: true,
286
+ composed: true
287
+ }));
288
+ }
289
+ onChartTypeChange(type) {
290
+ this.selectedChartType = type;
291
+ this.generateChartData();
292
+ }
293
+ onRegionClick(region) {
294
+ this.dispatchEvent(new CustomEvent('region-click', {
295
+ detail: { region },
296
+ bubbles: true,
297
+ composed: true
298
+ }));
299
+ }
300
+ downloadExcel() {
301
+ this.dispatchEvent(new CustomEvent('download-excel', {
302
+ bubbles: true,
303
+ composed: true
304
+ }));
305
+ }
306
+ // regionOrder에 따라 지역 데이터를 정렬
307
+ getSortedRegionData() {
308
+ // 실제 데이터를 기반으로 각 시도에 대한 데이터 생성
309
+ return this.regionOrder.map(regionName => {
310
+ const stat = this.regionalKpiStats.find(s => s.geoGroup === regionName);
311
+ const changeRate = this.calculateChangeRate(regionName);
312
+ return {
313
+ region: regionName,
314
+ kpi: stat ? (stat.avgVal * 20).toFixed(1) : '-',
315
+ change: changeRate.toFixed(2),
316
+ trendData: this.getRegionTrendData(regionName),
317
+ lat: 36.5,
318
+ lng: 127.5
319
+ };
320
+ });
321
+ }
322
+ getChangeRateClass(change) {
323
+ if (change > 0)
324
+ return 'change-up';
325
+ if (change < 0)
326
+ return 'change-down';
327
+ return 'change-neutral';
328
+ }
329
+ getChangeIcon(change) {
330
+ if (change > 0)
331
+ return '▲';
332
+ if (change < 0)
333
+ return '▼';
334
+ return '─';
335
+ }
336
+ render() {
337
+ return html `
338
+ <div class="panel-header">
339
+ <div class="panel-title">전국 KPI</div>
340
+ <button class="panel-close" style="visibility: hidden;">×</button>
341
+ </div>
342
+ <div class="panel-content">
343
+ <!-- KPI 카테고리 선택 -->
344
+ <select class="category-select" .value=${this.selectedCategory} @change=${this.onCategoryChange}>
345
+ <option value="전체 KPI">전체 KPI</option>
346
+ <option value="일정 성과">일정 성과</option>
347
+ <option value="비용 성과">비용 성과</option>
348
+ <option value="품질 성과">품질 성과</option>
349
+ <option value="안전 성과">안전 성과</option>
350
+ <option value="환경 성과">환경 성과</option>
351
+ <option value="생산성 성과">생산성 성과</option>
352
+ </select>
353
+
354
+ <!-- 종합 성과 -->
355
+ <div class="chart-section">
356
+ <div class="sub-title">종합 성과</div>
357
+ <div class="chart-toggle">
358
+ <button
359
+ class="toggle-button ${this.selectedChartType === 'boxplot' ? 'active' : ''}"
360
+ @click=${() => this.onChartTypeChange('boxplot')}
361
+ >
362
+ 박스플롯
363
+ </button>
364
+ <button
365
+ class="toggle-button ${this.selectedChartType === 'radar' ? 'active' : ''}"
366
+ @click=${() => this.onChartTypeChange('radar')}
367
+ >
368
+ 레이더차트
369
+ </button>
370
+ </div>
371
+ <div class="chart-container">
372
+ ${this.selectedChartType === 'boxplot'
373
+ ? html ` <sv-kpi-boxplot-chart .data=${this.chartData} .valueKey=${'value'}></sv-kpi-boxplot-chart> `
374
+ : html `
375
+ <sv-kpi-radar-chart
376
+ .data=${this.chartData}
377
+ .categories=${this.chartCategories}
378
+ .valueKey=${'value'}
379
+ ></sv-kpi-radar-chart>
380
+ `}
381
+ </div>
382
+ </div>
383
+
384
+ <!-- 시도별 성과 -->
385
+ <div class="chart-section">
386
+ <div class="sub-title">시도별 성과</div>
387
+ <table class="performance-table">
388
+ <thead>
389
+ <tr>
390
+ <th>지역명</th>
391
+ <th>KPI</th>
392
+ <th>변동률(%)</th>
393
+ <th>성과 추이</th>
394
+ </tr>
395
+ </thead>
396
+ <tbody>
397
+ ${this.getSortedRegionData().map((item) => html `
398
+ <tr
399
+ style="cursor: pointer; transition: background-color 0.2s;"
400
+ @click=${() => this.onRegionClick(item.region)}
401
+ @mouseenter=${(e) => (e.target.style.backgroundColor = '#f8f9fa')}
402
+ @mouseleave=${(e) => (e.target.style.backgroundColor = '')}
403
+ >
404
+ <td>${item.region}</td>
405
+ <td>${item.kpi}</td>
406
+ <td>
407
+ <div class="change-rate ${this.getChangeRateClass(item.change)}">
408
+ ${this.getChangeIcon(item.change)}${Math.abs(item.change)}%
409
+ </div>
410
+ </td>
411
+ <td>
412
+ ${item.trendData && item.trendData.length > 0
413
+ ? html `
414
+ <sv-kpi-mini-trend-chart
415
+ .data=${item.trendData}
416
+ .width=${60}
417
+ .height=${30}
418
+ .lineColor=${'#2196f3'}
419
+ .strokeWidth=${1.5}
420
+ .showPoints=${true}
421
+ .pointRadius=${1.5}
422
+ ></sv-kpi-mini-trend-chart>
423
+ `
424
+ : html `<span style="color: #999;">-</span>`}
425
+ </td>
426
+ </tr>
427
+ `)}
428
+ </tbody>
429
+ </table>
430
+ <button class="download-button" @click=${this.downloadExcel}>📊 엑셀 다운로드</button>
431
+ </div>
432
+ </div>
433
+ `;
434
+ }
435
+ };
436
+ KpiLeftPanel.styles = [
437
+ ScrollbarStyles,
438
+ css `
439
+ :host {
440
+ display: block;
441
+ width: 400px;
442
+ background: #fff;
443
+ border-right: 1px solid #e0e0e0;
444
+ overflow: hidden;
445
+ display: flex;
446
+ flex-direction: column;
447
+ box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
448
+ }
449
+ .panel-content {
450
+ padding: 20px;
451
+ overflow-y: auto;
452
+ flex: 1;
453
+ }
454
+ .panel-header {
455
+ display: flex;
456
+ justify-content: space-between;
457
+ align-items: center;
458
+ padding: 20px;
459
+ border-bottom: 1px solid #e0e0e0;
460
+ background: #fff;
461
+ height: 70px;
462
+ box-sizing: border-box;
463
+ }
464
+ .panel-title {
465
+ font-size: 1.3rem;
466
+ font-weight: bold;
467
+ color: #333;
468
+ margin: 0;
469
+ }
470
+ .panel-close {
471
+ width: 32px;
472
+ height: 32px;
473
+ border: none;
474
+ background: #fff;
475
+ border-radius: 50%;
476
+ cursor: pointer;
477
+ display: flex;
478
+ align-items: center;
479
+ justify-content: center;
480
+ font-size: 1.2rem;
481
+ color: #666;
482
+ transition: all 0.2s;
483
+ }
484
+ .panel-close:hover {
485
+ background: #e9ecef;
486
+ color: #333;
487
+ }
488
+ .sub-title {
489
+ font-size: 1rem;
490
+ font-weight: 600;
491
+ margin-bottom: 16px;
492
+ color: #495057;
493
+ }
494
+ .chart-section {
495
+ background: #f8f9fa;
496
+ border-radius: 8px;
497
+ padding: 16px;
498
+ margin-bottom: 20px;
499
+ }
500
+ .chart-toggle {
501
+ display: flex;
502
+ gap: 8px;
503
+ margin-bottom: 16px;
504
+ }
505
+ .toggle-button {
506
+ padding: 8px 16px;
507
+ border: 1px solid #ced4da;
508
+ background: #fff;
509
+ border-radius: 6px;
510
+ cursor: pointer;
511
+ font-size: 0.9rem;
512
+ transition: all 0.2s;
513
+ }
514
+ .toggle-button.active {
515
+ background: #667eea;
516
+ color: white;
517
+ border-color: #667eea;
518
+ }
519
+ .chart-container {
520
+ height: 300px;
521
+ display: flex;
522
+ align-items: center;
523
+ justify-content: center;
524
+ background: white;
525
+ border-radius: 6px;
526
+ border: 1px solid #e9ecef;
527
+ }
528
+ .performance-table {
529
+ width: 100%;
530
+ border-collapse: collapse;
531
+ margin-top: 16px;
532
+ }
533
+ .performance-table th,
534
+ .performance-table td {
535
+ padding: 12px 8px;
536
+ text-align: left;
537
+ border-bottom: 1px solid #e9ecef;
538
+ }
539
+ .performance-table th {
540
+ background: #f8f9fa;
541
+ font-weight: 600;
542
+ color: #495057;
543
+ }
544
+ .performance-table td {
545
+ color: #333;
546
+ }
547
+ .change-rate {
548
+ display: flex;
549
+ align-items: center;
550
+ gap: 4px;
551
+ }
552
+ .change-up {
553
+ color: #dc3545;
554
+ }
555
+ .change-down {
556
+ color: #198754;
557
+ }
558
+ .change-neutral {
559
+ color: #6c757d;
560
+ }
561
+ .trend-chart {
562
+ width: 60px;
563
+ height: 30px;
564
+ background: #f8f9fa;
565
+ border-radius: 4px;
566
+ display: flex;
567
+ align-items: center;
568
+ justify-content: center;
569
+ font-size: 0.8rem;
570
+ color: #666;
571
+ }
572
+ .download-button {
573
+ margin-top: 16px;
574
+ padding: 8px 16px;
575
+ background: #28a745;
576
+ color: white;
577
+ border: none;
578
+ border-radius: 6px;
579
+ cursor: pointer;
580
+ font-size: 0.9rem;
581
+ display: flex;
582
+ align-items: center;
583
+ gap: 8px;
584
+ }
585
+ .download-button:hover {
586
+ background: #218838;
587
+ }
588
+ .category-select {
589
+ width: 100%;
590
+ padding: 8px 12px;
591
+ border: 1px solid #ced4da;
592
+ border-radius: 6px;
593
+ background: white;
594
+ margin-bottom: 20px;
595
+ }
596
+ `
597
+ ];
598
+ __decorate([
599
+ property({ type: String }),
600
+ __metadata("design:type", Object)
601
+ ], KpiLeftPanel.prototype, "selectedCategory", void 0);
602
+ __decorate([
603
+ property({ type: String }),
604
+ __metadata("design:type", Object)
605
+ ], KpiLeftPanel.prototype, "selectedChartType", void 0);
606
+ __decorate([
607
+ property({ type: Array }),
608
+ __metadata("design:type", Array)
609
+ ], KpiLeftPanel.prototype, "mapData", void 0);
610
+ __decorate([
611
+ property({ type: String }),
612
+ __metadata("design:type", Object)
613
+ ], KpiLeftPanel.prototype, "startYearMonth", void 0);
614
+ __decorate([
615
+ property({ type: String }),
616
+ __metadata("design:type", Object)
617
+ ], KpiLeftPanel.prototype, "endYearMonth", void 0);
618
+ __decorate([
619
+ state(),
620
+ __metadata("design:type", Array)
621
+ ], KpiLeftPanel.prototype, "chartData", void 0);
622
+ __decorate([
623
+ state(),
624
+ __metadata("design:type", Array)
625
+ ], KpiLeftPanel.prototype, "chartCategories", void 0);
626
+ __decorate([
627
+ state(),
628
+ __metadata("design:type", Array)
629
+ ], KpiLeftPanel.prototype, "kpiYComprehensiveStats", void 0);
630
+ __decorate([
631
+ state(),
632
+ __metadata("design:type", Array)
633
+ ], KpiLeftPanel.prototype, "regionalKpiStats", void 0);
634
+ __decorate([
635
+ state(),
636
+ __metadata("design:type", Array)
637
+ ], KpiLeftPanel.prototype, "monthlyTrendData", void 0);
638
+ KpiLeftPanel = __decorate([
639
+ customElement('kpi-left-panel')
640
+ ], KpiLeftPanel);
641
+ export { KpiLeftPanel };
642
+ //# sourceMappingURL=kpi-left-panel.js.map