@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,731 @@
1
+ import { __decorate, __metadata } from "tslib";
2
+ import '@material/web/icon/icon.js';
3
+ import '@material/web/button/elevated-button.js';
4
+ import '@material/web/textfield/outlined-text-field.js';
5
+ import { CommonButtonStyles, CommonHeaderStyles, ScrollbarStyles } from '@operato/styles';
6
+ import { PageView, store } from '@operato/shell';
7
+ import { css, html } from 'lit';
8
+ import { customElement, property, state } from 'lit/decorators.js';
9
+ import { ScopedElementsMixin } from '@open-wc/scoped-elements';
10
+ import { client } from '@operato/graphql';
11
+ import { i18next, localize } from '@operato/i18n';
12
+ import { notify } from '@operato/layout';
13
+ import { connect } from 'pwa-helpers/connect-mixin';
14
+ import gql from 'graphql-tag';
15
+ let KpiMetricValueEditorPage = class KpiMetricValueEditorPage extends connect(store)(localize(i18next)(ScopedElementsMixin(PageView))) {
16
+ constructor() {
17
+ super(...arguments);
18
+ this.org = '';
19
+ this.startDate = '';
20
+ this.endDate = '';
21
+ this.metrics = [];
22
+ this.dates = [];
23
+ this.loading = false;
24
+ this.error = '';
25
+ this.editingCell = null;
26
+ this._existingValues = [];
27
+ }
28
+ get context() {
29
+ return {
30
+ title: i18next.t('title.kpi metric value editor'),
31
+ actions: [
32
+ Object.assign({ title: i18next.t('button.save'), action: this._saveValues.bind(this) }, CommonButtonStyles.save),
33
+ Object.assign({ title: i18next.t('button.cancel'), action: this._cancel.bind(this) }, CommonButtonStyles.cancel)
34
+ ]
35
+ };
36
+ }
37
+ render() {
38
+ if (this.loading) {
39
+ return html `
40
+ <div class="loading">
41
+ <md-icon>hourglass_empty</md-icon>
42
+ <span>데이터를 불러오는 중...</span>
43
+ </div>
44
+ `;
45
+ }
46
+ if (this.error) {
47
+ return html `
48
+ <div class="error">
49
+ <md-icon>error</md-icon>
50
+ <span>${this.error}</span>
51
+ </div>
52
+ `;
53
+ }
54
+ return html `
55
+ <div class="header">
56
+ <div class="controls">
57
+ <md-outlined-text-field
58
+ label="그룹"
59
+ .value=${this.org}
60
+ @input=${(e) => (this.org = e.target.value)}
61
+ style="width: 150px;"
62
+ ></md-outlined-text-field>
63
+
64
+ <md-outlined-text-field
65
+ label="시작일"
66
+ type="date"
67
+ .value=${this.startDate}
68
+ @input=${(e) => (this.startDate = e.target.value)}
69
+ style="width: 150px;"
70
+ ></md-outlined-text-field>
71
+
72
+ <md-outlined-text-field
73
+ label="종료일"
74
+ type="date"
75
+ .value=${this.endDate}
76
+ @input=${(e) => (this.endDate = e.target.value)}
77
+ style="width: 150px;"
78
+ ></md-outlined-text-field>
79
+
80
+ <md-elevated-button @click=${this._loadData}>
81
+ <md-icon slot="icon">refresh</md-icon>
82
+ 새로고침
83
+ </md-elevated-button>
84
+ </div>
85
+ </div>
86
+
87
+ <div class="table-container">
88
+ <table style="border-collapse: collapse; width: 100%;">
89
+ <thead>
90
+ <tr>
91
+ <th
92
+ style="border: 1px solid #ccc; padding: 8px; background: #f5f5f5; min-width: 200px; left: 0; top: 0;"
93
+ class="metric-header"
94
+ >
95
+ Metric명
96
+ </th>
97
+ ${this.dates.map(date => html `
98
+ <th
99
+ style="border: 1px solid #ccc; padding: 8px; background: #f5f5f5; min-width: 80px; position: sticky; top: 0;"
100
+ >
101
+ ${this._formatDate(date)}
102
+ </th>
103
+ `)}
104
+ </tr>
105
+ </thead>
106
+ <tbody>
107
+ ${this.metrics.map(metric => html `
108
+ <tr>
109
+ <td
110
+ style="border: 1px solid #ccc; padding: 8px; background: #fff; min-width: 200px;"
111
+ class="metric-name"
112
+ >
113
+ ${metric.metricName}
114
+ <span
115
+ style="font-size: 10px; padding: 2px 6px; background: #e0e0e0; border-radius: 4px; margin-left: 8px;"
116
+ >${metric.periodType}</span
117
+ >
118
+ </td>
119
+ ${this.dates.map(date => html `
120
+ <td
121
+ style="border: 1px solid #ccc; padding: 8px; background: #e3f2fd; text-align: center; min-width: 80px; cursor: pointer;"
122
+ @click=${() => this._startEdit(metric.metricId, date)}
123
+ >
124
+ ${this._renderCellContent(metric, date)}
125
+ </td>
126
+ `)}
127
+ </tr>
128
+ `)}
129
+ </tbody>
130
+ </table>
131
+ </div>
132
+
133
+ <div class="legend">
134
+ <div class="legend-item">
135
+ <div class="legend-color" style="background: var(--md-sys-color-primary-container);"></div>
136
+ <span>편집 가능</span>
137
+ </div>
138
+ <div class="legend-item">
139
+ <div class="legend-color" style="background: var(--md-sys-color-secondary-container);"></div>
140
+ <span>하이라이트 (PeriodType에 따라)</span>
141
+ </div>
142
+ <div class="legend-item">
143
+ <div class="legend-color" style="background: var(--md-sys-color-surface-container);"></div>
144
+ <span>편집 불가</span>
145
+ </div>
146
+ </div>
147
+ `;
148
+ }
149
+ _renderCellContent(metric, date) {
150
+ var _a, _b, _c, _d, _e, _f, _g, _h;
151
+ const isEditing = ((_a = this.editingCell) === null || _a === void 0 ? void 0 : _a.metricId) === metric.metricId && ((_b = this.editingCell) === null || _b === void 0 ? void 0 : _b.date) === date;
152
+ // periodType에 따라 값을 가져오는 방식 결정
153
+ let value = null;
154
+ if (metric.periodType === 'DAY') {
155
+ // DAY인 경우 해당 날짜의 값을 사용
156
+ value = (_c = metric.values[date]) === null || _c === void 0 ? void 0 : _c.value;
157
+ }
158
+ else if (metric.periodType === 'WEEK') {
159
+ // WEEK인 경우 해당 주의 월요일의 값을 사용
160
+ const weekKey = this._getWeekKey(date);
161
+ value = (_d = metric.values[weekKey]) === null || _d === void 0 ? void 0 : _d.value;
162
+ }
163
+ else if (metric.periodType === 'MONTH') {
164
+ // MONTH인 경우 해당 월의 값을 사용
165
+ const monthKey = this._getMonthKey(date);
166
+ value = (_e = metric.values[monthKey]) === null || _e === void 0 ? void 0 : _e.value;
167
+ }
168
+ else if (metric.periodType === 'QUARTER') {
169
+ // QUARTER인 경우 해당 분기의 값을 사용
170
+ const quarterKey = this._getQuarterKey(date);
171
+ value = (_f = metric.values[quarterKey]) === null || _f === void 0 ? void 0 : _f.value;
172
+ }
173
+ else if (metric.periodType === 'YEAR') {
174
+ // YEAR인 경우 해당 연도의 값을 사용
175
+ const yearKey = this._getYearKey(date);
176
+ value = (_g = metric.values[yearKey]) === null || _g === void 0 ? void 0 : _g.value;
177
+ }
178
+ else {
179
+ // 기본값: DAY와 동일
180
+ value = (_h = metric.values[date]) === null || _h === void 0 ? void 0 : _h.value;
181
+ }
182
+ if (isEditing) {
183
+ console.log('Rendering input for editing, value:', value);
184
+ return html `
185
+ <input
186
+ type="number"
187
+ step="0.01"
188
+ .value=${(value || '').toString()}
189
+ @blur=${(e) => this._finishEdit(metric.metricId, date, parseFloat(e.target.value) || 0)}
190
+ @keydown=${(e) => e.key === 'Enter' && e.target.blur()}
191
+ style="width: 100%; text-align: center; border: none; background: transparent;"
192
+ autofocus
193
+ />
194
+ `;
195
+ }
196
+ return html `
197
+ <div style="display: flex; flex-direction: column; gap: 2px;">
198
+ <span style="font-weight: 500; color: ${value !== null && value !== undefined ? 'inherit' : '#999'};">
199
+ ${value !== null && value !== undefined ? value.toLocaleString() : '클릭하여 입력'}
200
+ </span>
201
+ </div>
202
+ `;
203
+ }
204
+ _formatDate(date) {
205
+ const d = new Date(date);
206
+ return d.toLocaleDateString('ko-KR', { month: 'numeric', day: 'numeric' });
207
+ }
208
+ _startEdit(metricId, date) {
209
+ this.editingCell = { metricId, date };
210
+ }
211
+ _finishEdit(metricId, date, value) {
212
+ var _a;
213
+ const metric = this.metrics.find(m => m.metricId === metricId);
214
+ if (metric) {
215
+ // periodType에 따라 저장할 키 결정
216
+ let storageKey = date;
217
+ if (metric.periodType === 'WEEK') {
218
+ storageKey = this._getWeekKey(date);
219
+ }
220
+ else if (metric.periodType === 'MONTH') {
221
+ storageKey = this._getMonthKey(date);
222
+ }
223
+ else if (metric.periodType === 'QUARTER') {
224
+ storageKey = this._getQuarterKey(date);
225
+ }
226
+ else if (metric.periodType === 'YEAR') {
227
+ storageKey = this._getYearKey(date);
228
+ }
229
+ if (!metric.values[storageKey]) {
230
+ metric.values[storageKey] = { value: 0, isDirty: false };
231
+ }
232
+ // 값이 변경되었는지 확인
233
+ const originalValue = (_a = this._findExistingValue(metricId, storageKey)) === null || _a === void 0 ? void 0 : _a.value;
234
+ const isChanged = originalValue !== value;
235
+ metric.values[storageKey].value = value;
236
+ metric.values[storageKey].isDirty = isChanged;
237
+ }
238
+ this.editingCell = null;
239
+ }
240
+ async _loadData() {
241
+ if (!this.startDate || !this.endDate) {
242
+ this.error = '시작일, 종료일을 모두 입력해주세요.';
243
+ return;
244
+ }
245
+ this.loading = true;
246
+ this.error = '';
247
+ try {
248
+ // KPI Metric 목록 조회
249
+ const metricsResponse = await client.query({
250
+ query: gql `
251
+ query ($filters: [Filter!]) {
252
+ kpiMetrics(filters: $filters) {
253
+ items {
254
+ id
255
+ name
256
+ periodType
257
+ active
258
+ }
259
+ total
260
+ }
261
+ }
262
+ `,
263
+ variables: {
264
+ filters: [{ name: 'active', operator: 'eq', value: true }]
265
+ }
266
+ });
267
+ // KPI Metric Value 데이터 조회
268
+ const valuesResponse = await client.query({
269
+ query: gql `
270
+ query ($filters: [Filter!]) {
271
+ kpiMetricValues(filters: $filters) {
272
+ items {
273
+ id
274
+ metricId
275
+ valueDate
276
+ value
277
+ org
278
+ }
279
+ total
280
+ }
281
+ }
282
+ `,
283
+ variables: {
284
+ filters: [
285
+ ...(this.org ? [{ name: 'org', operator: 'eq', value: this.org }] : []),
286
+ { name: 'valueDate', operator: 'between', value: [this.startDate, this.endDate] }
287
+ ]
288
+ }
289
+ });
290
+ // 날짜 배열 생성
291
+ this.dates = this._generateDateArray(this.startDate, this.endDate);
292
+ // KPI Metric 목록을 기준으로 데이터 구성
293
+ this.metrics = metricsResponse.data.kpiMetrics.items.map((metric) => ({
294
+ metricId: metric.id,
295
+ metricName: metric.name,
296
+ periodType: metric.periodType,
297
+ values: {}
298
+ }));
299
+ // 기존 KPI Metric Value 데이터 저장
300
+ this._existingValues = valuesResponse.data.kpiMetricValues.items;
301
+ // KPI Metric Value 데이터를 해당 Metric에 매핑
302
+ valuesResponse.data.kpiMetricValues.items.forEach((value) => {
303
+ const metric = this.metrics.find(m => m.metricId === value.metricId);
304
+ if (metric) {
305
+ metric.values[value.valueDate] = {
306
+ value: value.value,
307
+ isDirty: false
308
+ };
309
+ }
310
+ });
311
+ // KPI Metric Value가 없는 Metric도 빈 값으로 초기화
312
+ this.metrics.forEach(metric => {
313
+ this.dates.forEach(date => {
314
+ if (!metric.values[date]) {
315
+ metric.values[date] = { value: null, isDirty: false };
316
+ }
317
+ });
318
+ });
319
+ this.requestUpdate();
320
+ }
321
+ catch (error) {
322
+ console.error('데이터 로드 중 오류:', error);
323
+ this.error = '데이터를 불러오는 중 오류가 발생했습니다.';
324
+ }
325
+ finally {
326
+ this.loading = false;
327
+ }
328
+ }
329
+ _generateDateArray(startDate, endDate) {
330
+ const dates = [];
331
+ const start = new Date(startDate);
332
+ const end = new Date(endDate);
333
+ // 일별로 생성 (기본)
334
+ for (let d = new Date(start); d <= end; d.setDate(d.getDate() + 1)) {
335
+ dates.push(d.toLocaleDateString('sv-SE'));
336
+ }
337
+ return dates;
338
+ }
339
+ async _saveValues() {
340
+ try {
341
+ const patches = [];
342
+ this.metrics.forEach(metric => {
343
+ Object.entries(metric.values).forEach(([date, data]) => {
344
+ // dirty 상태인 데이터만 처리
345
+ if (data.isDirty && data.value !== null && data.value !== undefined) {
346
+ const existingValue = this._findExistingValue(metric.metricId, date);
347
+ if (existingValue) {
348
+ patches.push({
349
+ id: existingValue.id,
350
+ value: data.value,
351
+ org: this.org,
352
+ cuFlag: 'M'
353
+ });
354
+ }
355
+ else {
356
+ console.log('Creating new value for metric:', metric.metricName, 'periodType:', metric.periodType, 'date:', date);
357
+ const normalizedDate = this._normalizeDateByPeriodType(date, metric.periodType);
358
+ console.log('Normalized date:', normalizedDate);
359
+ patches.push({
360
+ metricId: metric.metricId,
361
+ valueDate: normalizedDate,
362
+ value: data.value,
363
+ org: this.org,
364
+ cuFlag: '+'
365
+ });
366
+ }
367
+ }
368
+ });
369
+ });
370
+ if (patches.length === 0) {
371
+ notify({ message: '저장할 데이터가 없습니다.' });
372
+ return;
373
+ }
374
+ // 디버깅: patches 내용 확인
375
+ console.log('Sending patches:', patches);
376
+ // 단일 mutation으로 생성과 업데이트 처리
377
+ const response = await client.mutate({
378
+ mutation: gql `
379
+ mutation ($patches: [KpiMetricValuePatch!]!) {
380
+ updateMultipleKpiMetricValue(patches: $patches) {
381
+ id
382
+ value
383
+ valueDate
384
+ metricId
385
+ }
386
+ }
387
+ `,
388
+ variables: { patches }
389
+ });
390
+ if (!response.errors) {
391
+ // 저장 후 dirty 상태 해제
392
+ response.data.updateMultipleKpiMetricValue.forEach((savedValue) => {
393
+ const metric = this.metrics.find(m => m.metricId === savedValue.metricId);
394
+ if (metric && metric.values[savedValue.valueDate]) {
395
+ metric.values[savedValue.valueDate].isDirty = false;
396
+ }
397
+ });
398
+ notify({ message: 'KPI Metric 값이 성공적으로 저장되었습니다.' });
399
+ this.requestUpdate();
400
+ }
401
+ }
402
+ catch (error) {
403
+ console.error('저장 중 오류:', error);
404
+ notify({ message: '저장 중 오류가 발생했습니다.' });
405
+ }
406
+ }
407
+ _findExistingValue(metricId, date) {
408
+ var _a;
409
+ // 기존 로드된 KPI Metric Value 데이터에서 찾기
410
+ return (_a = this._existingValues) === null || _a === void 0 ? void 0 : _a.find((v) => v.metricId === metricId && v.valueDate === date);
411
+ }
412
+ _getMonthKey(date) {
413
+ const dateObj = new Date(date);
414
+ return `${dateObj.getFullYear()}-${String(dateObj.getMonth() + 1).padStart(2, '0')}`;
415
+ }
416
+ _getQuarterKey(date) {
417
+ const dateObj = new Date(date);
418
+ const quarter = Math.floor(dateObj.getMonth() / 3) + 1;
419
+ return `${dateObj.getFullYear()}-Q${quarter}`;
420
+ }
421
+ _getYearKey(date) {
422
+ const dateObj = new Date(date);
423
+ return `${dateObj.getFullYear()}`;
424
+ }
425
+ _getWeekKey(date) {
426
+ const dateObj = new Date(date);
427
+ const dayOfWeek = dateObj.getDay();
428
+ const daysToMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
429
+ const monday = new Date(dateObj);
430
+ monday.setDate(dateObj.getDate() - daysToMonday);
431
+ return monday.toLocaleDateString('sv-SE');
432
+ }
433
+ _getMondayOfWeek(date) {
434
+ const dateObj = new Date(date);
435
+ const dayOfWeek = dateObj.getDay();
436
+ const daysToMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1;
437
+ const monday = new Date(dateObj);
438
+ monday.setDate(dateObj.getDate() - daysToMonday);
439
+ return monday.toLocaleDateString('sv-SE');
440
+ }
441
+ _getFirstDayOfMonth(date) {
442
+ const dateObj = new Date(date);
443
+ return new Date(dateObj.getFullYear(), dateObj.getMonth(), 1).toLocaleDateString('sv-SE');
444
+ }
445
+ _getFirstDayOfQuarter(date) {
446
+ const dateObj = new Date(date);
447
+ const quarter = Math.floor(dateObj.getMonth() / 3);
448
+ const firstMonthOfQuarter = quarter * 3;
449
+ return new Date(dateObj.getFullYear(), firstMonthOfQuarter, 1).toLocaleDateString('sv-SE');
450
+ }
451
+ _getFirstDayOfYear(date) {
452
+ const dateObj = new Date(date);
453
+ return new Date(dateObj.getFullYear(), 0, 1).toLocaleDateString('sv-SE');
454
+ }
455
+ _normalizeDateByPeriodType(date, periodType) {
456
+ console.log('Normalizing date:', date, 'for periodType:', periodType);
457
+ const dateObj = new Date(date);
458
+ switch (periodType) {
459
+ case 'DAY':
460
+ console.log('DAY - returning original date:', date);
461
+ return date; // 그대로 사용
462
+ case 'WEEK':
463
+ // 해당 주의 월요일로 설정
464
+ const dayOfWeek = dateObj.getDay();
465
+ const daysToMonday = dayOfWeek === 0 ? 6 : dayOfWeek - 1; // 일요일이면 6일 전, 아니면 dayOfWeek - 1
466
+ const monday = new Date(dateObj);
467
+ monday.setDate(dateObj.getDate() - daysToMonday);
468
+ const weeklyDate = monday.toLocaleDateString('sv-SE');
469
+ console.log('WEEK - returning monday:', weeklyDate);
470
+ return weeklyDate;
471
+ case 'MONTH':
472
+ // 년-월 형식으로 설정 (예: "2025-01")
473
+ const monthlyDate = `${dateObj.getFullYear()}-${String(dateObj.getMonth() + 1).padStart(2, '0')}`;
474
+ console.log('MONTH - returning year-month format:', monthlyDate);
475
+ return monthlyDate;
476
+ case 'QUARTER':
477
+ // 분기 형식으로 설정 (예: "2025-Q1")
478
+ const quarter = Math.floor(dateObj.getMonth() / 3) + 1;
479
+ const quarterlyDate = `${dateObj.getFullYear()}-Q${quarter}`;
480
+ console.log('QUARTER - returning quarter format:', quarterlyDate);
481
+ return quarterlyDate;
482
+ case 'YEAR':
483
+ // 년도 형식으로 설정 (예: "2025")
484
+ const yearlyDate = `${dateObj.getFullYear()}`;
485
+ console.log('YEAR - returning year format:', yearlyDate);
486
+ return yearlyDate;
487
+ default:
488
+ console.log('DEFAULT - returning original date:', date);
489
+ return date; // 기본값
490
+ }
491
+ }
492
+ _cancel() {
493
+ // 편집 취소 로직
494
+ this.editingCell = null;
495
+ this._loadData(); // 원본 데이터로 복원
496
+ }
497
+ async pageInitialized(lifecycle) {
498
+ // 기본값 설정 - 지난 1개월
499
+ if (!this.startDate) {
500
+ const today = new Date();
501
+ const oneMonthAgo = new Date(today.getFullYear(), today.getMonth() - 1, today.getDate());
502
+ this.startDate = oneMonthAgo.toLocaleDateString('sv-SE');
503
+ }
504
+ if (!this.endDate) {
505
+ const today = new Date();
506
+ this.endDate = today.toLocaleDateString('sv-SE');
507
+ }
508
+ // 페이지 초기화 시 자동으로 데이터 로드
509
+ await this._loadData();
510
+ }
511
+ };
512
+ KpiMetricValueEditorPage.styles = [
513
+ CommonHeaderStyles,
514
+ ScrollbarStyles,
515
+ css `
516
+ :host {
517
+ display: flex;
518
+ flex-direction: column;
519
+ padding: 20px;
520
+ overflow-x: auto;
521
+ }
522
+
523
+ .header {
524
+ display: flex;
525
+ gap: 16px;
526
+ align-items: center;
527
+ margin-bottom: 20px;
528
+ padding: 16px;
529
+ background: var(--md-sys-color-surface-container);
530
+ border-radius: 8px;
531
+ }
532
+
533
+ .controls {
534
+ display: flex;
535
+ gap: 12px;
536
+ align-items: center;
537
+ }
538
+
539
+ .table-container {
540
+ flex: 1;
541
+ overflow: auto;
542
+ border: 1px solid var(--md-sys-color-outline);
543
+ border-radius: 8px;
544
+ }
545
+
546
+ table {
547
+ width: 100%;
548
+ border-collapse: collapse;
549
+ min-width: max-content;
550
+ }
551
+
552
+ th {
553
+ background: var(--md-sys-color-surface-container-low);
554
+ font-weight: 500;
555
+ padding: 8px 12px;
556
+ border: 1px solid var(--md-sys-color-outline-variant);
557
+ min-width: 80px;
558
+ height: 120px;
559
+ vertical-align: middle;
560
+ }
561
+
562
+ td {
563
+ padding: 8px 12px;
564
+ border: 1px solid var(--md-sys-color-outline-variant);
565
+ min-width: 80px;
566
+ height: 60px;
567
+ text-align: right;
568
+ vertical-align: middle;
569
+ }
570
+
571
+ .metric-header {
572
+ position: sticky;
573
+ left: 0;
574
+ top: 0;
575
+ z-index: 3;
576
+ }
577
+
578
+ .metric-name {
579
+ position: sticky;
580
+ left: 0;
581
+ background: var(--md-sys-color-surface);
582
+ font-weight: 500;
583
+ min-width: 200px;
584
+ text-align: left;
585
+ z-index: 2;
586
+ }
587
+
588
+ tr:hover {
589
+ background: var(--md-sys-color-surface-container-high);
590
+ }
591
+
592
+ th.date-header {
593
+ position: sticky;
594
+ top: 0;
595
+ z-index: 2;
596
+ text-align: center;
597
+ font-size: 11px;
598
+ color: var(--md-sys-color-on-surface-variant);
599
+ writing-mode: vertical-rl;
600
+ text-orientation: mixed;
601
+ min-height: 120px;
602
+ display: flex;
603
+ align-items: center;
604
+ justify-content: center;
605
+ }
606
+
607
+ td.editable-cell {
608
+ cursor: pointer;
609
+ background: var(--md-sys-color-primary-container);
610
+ color: var(--md-sys-color-on-primary-container);
611
+ }
612
+
613
+ td.editable-cell:hover {
614
+ background: var(--md-sys-color-primary-container-high);
615
+ }
616
+
617
+ td.highlighted-cell {
618
+ background: var(--md-sys-color-secondary-container);
619
+ color: var(--md-sys-color-on-secondary-container);
620
+ font-weight: 500;
621
+ }
622
+
623
+ td.highlighted-cell:hover {
624
+ background: var(--md-sys-color-secondary-container-high);
625
+ }
626
+
627
+ td.disabled-cell {
628
+ background: var(--md-sys-color-surface-container);
629
+ color: var(--md-sys-color-on-surface-variant);
630
+ cursor: not-allowed;
631
+ }
632
+
633
+ .value-input {
634
+ width: 100%;
635
+ text-align: center;
636
+ border: none;
637
+ background: transparent;
638
+ color: inherit;
639
+ font-size: 12px;
640
+ padding: 2px;
641
+ }
642
+
643
+ .value-input:focus {
644
+ outline: 2px solid var(--md-sys-color-primary);
645
+ border-radius: 4px;
646
+ }
647
+
648
+ .period-type-badge {
649
+ font-size: 10px;
650
+ padding: 2px 6px;
651
+ border-radius: 4px;
652
+ background: var(--md-sys-color-tertiary-container);
653
+ color: var(--md-sys-color-on-tertiary-container);
654
+ margin-left: 8px;
655
+ }
656
+
657
+ .loading {
658
+ display: flex;
659
+ justify-content: center;
660
+ align-items: center;
661
+ height: 200px;
662
+ color: var(--md-sys-color-on-surface-variant);
663
+ }
664
+
665
+ .error {
666
+ color: var(--md-sys-color-error);
667
+ padding: 16px;
668
+ text-align: center;
669
+ }
670
+
671
+ .legend {
672
+ display: flex;
673
+ gap: 16px;
674
+ margin-top: 16px;
675
+ font-size: 12px;
676
+ }
677
+
678
+ .legend-item {
679
+ display: flex;
680
+ align-items: center;
681
+ gap: 4px;
682
+ }
683
+
684
+ .legend-color {
685
+ width: 16px;
686
+ height: 16px;
687
+ border-radius: 2px;
688
+ }
689
+ `
690
+ ];
691
+ __decorate([
692
+ property({ type: String }),
693
+ __metadata("design:type", String)
694
+ ], KpiMetricValueEditorPage.prototype, "org", void 0);
695
+ __decorate([
696
+ property({ type: String }),
697
+ __metadata("design:type", String)
698
+ ], KpiMetricValueEditorPage.prototype, "startDate", void 0);
699
+ __decorate([
700
+ property({ type: String }),
701
+ __metadata("design:type", String)
702
+ ], KpiMetricValueEditorPage.prototype, "endDate", void 0);
703
+ __decorate([
704
+ state(),
705
+ __metadata("design:type", Array)
706
+ ], KpiMetricValueEditorPage.prototype, "metrics", void 0);
707
+ __decorate([
708
+ state(),
709
+ __metadata("design:type", Array)
710
+ ], KpiMetricValueEditorPage.prototype, "dates", void 0);
711
+ __decorate([
712
+ state(),
713
+ __metadata("design:type", Boolean)
714
+ ], KpiMetricValueEditorPage.prototype, "loading", void 0);
715
+ __decorate([
716
+ state(),
717
+ __metadata("design:type", String)
718
+ ], KpiMetricValueEditorPage.prototype, "error", void 0);
719
+ __decorate([
720
+ state(),
721
+ __metadata("design:type", Object)
722
+ ], KpiMetricValueEditorPage.prototype, "editingCell", void 0);
723
+ __decorate([
724
+ state(),
725
+ __metadata("design:type", Array)
726
+ ], KpiMetricValueEditorPage.prototype, "_existingValues", void 0);
727
+ KpiMetricValueEditorPage = __decorate([
728
+ customElement('kpi-metric-value-editor-page')
729
+ ], KpiMetricValueEditorPage);
730
+ export { KpiMetricValueEditorPage };
731
+ //# sourceMappingURL=kpi-metric-value-editor-page.js.map