@things-factory/kpi 9.0.31 → 9.0.33

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 (365) hide show
  1. package/README.md +1 -2
  2. package/client/charts/kpi-boxplot-chart.ts +182 -42
  3. package/client/charts/kpi-radar-chart.ts +9 -9
  4. package/client/pages/kpi/kpi-list-page.ts +196 -32
  5. package/client/pages/kpi/kpi-overview.ts +9 -11
  6. package/client/pages/kpi/kpi-tree-page.ts +409 -0
  7. package/client/pages/kpi/kpi-view.ts +187 -0
  8. package/client/pages/kpi-dashboard/cards/kpi-level1-card.ts +1 -1
  9. package/client/pages/kpi-dashboard/cards/kpi-level2-comparison.ts +1 -1
  10. package/client/pages/kpi-dashboard/cards/kpi-level3-comparison.ts +1 -1
  11. package/client/pages/kpi-dashboard/components/kpi-left-panel.ts +198 -160
  12. package/client/pages/kpi-dashboard/components/kpi-map-panel.ts +133 -0
  13. package/client/pages/kpi-dashboard/components/kpi-region-popup.ts +3 -2
  14. package/client/pages/kpi-dashboard/kpi-dashboard-map.ts +291 -48
  15. package/client/pages/kpi-dashboard/kpi-dashboard.ts +28 -30
  16. package/client/pages/kpi-history/kpi-history-list-page.ts +11 -11
  17. package/client/pages/kpi-metric/kpi-metric-list-page.ts +10 -2
  18. package/client/pages/kpi-metric-value/kpi-metric-value-editor-page.ts +7 -7
  19. package/client/pages/kpi-metric-value/kpi-metric-value-importer.ts +2 -2
  20. package/client/pages/kpi-metric-value/kpi-metric-value-list-page.ts +16 -8
  21. package/client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.ts +5 -5
  22. package/client/pages/kpi-statistic/kpi-statistic-editor-page.ts +1 -2
  23. package/client/pages/kpi-statistic/kpi-statistic-list-page.ts +10 -2
  24. package/client/pages/kpi-value/kpi-value-editor-page.ts +11 -7
  25. package/client/pages/kpi-value/kpi-value-list-page.ts +31 -7
  26. package/client/route.ts +2 -9
  27. package/design-entities.md +8 -12
  28. package/dist-client/charts/kpi-boxplot-chart.d.ts +2 -0
  29. package/dist-client/charts/kpi-boxplot-chart.js +168 -42
  30. package/dist-client/charts/kpi-boxplot-chart.js.map +1 -1
  31. package/dist-client/charts/kpi-radar-chart.js +9 -9
  32. package/dist-client/charts/kpi-radar-chart.js.map +1 -1
  33. package/dist-client/pages/kpi/kpi-list-page.d.ts +19 -3
  34. package/dist-client/pages/kpi/kpi-list-page.js +188 -32
  35. package/dist-client/pages/kpi/kpi-list-page.js.map +1 -1
  36. package/dist-client/pages/kpi/kpi-overview.js +9 -11
  37. package/dist-client/pages/kpi/kpi-overview.js.map +1 -1
  38. package/dist-client/pages/kpi/kpi-tree-page.d.ts +59 -0
  39. package/dist-client/pages/kpi/kpi-tree-page.js +403 -0
  40. package/dist-client/pages/kpi/kpi-tree-page.js.map +1 -0
  41. package/dist-client/pages/kpi/kpi-view.d.ts +12 -0
  42. package/dist-client/pages/kpi/kpi-view.js +191 -0
  43. package/dist-client/pages/kpi/kpi-view.js.map +1 -0
  44. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js +1 -1
  45. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js.map +1 -1
  46. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js +1 -1
  47. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js.map +1 -1
  48. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js +1 -1
  49. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js.map +1 -1
  50. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.d.ts +3 -1
  51. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js +197 -161
  52. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js.map +1 -1
  53. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.d.ts +5 -0
  54. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js +146 -0
  55. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js.map +1 -1
  56. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js +3 -2
  57. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js.map +1 -1
  58. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.d.ts +3 -1
  59. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js +268 -46
  60. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js.map +1 -1
  61. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js +28 -30
  62. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js.map +1 -1
  63. package/dist-client/pages/kpi-history/kpi-history-list-page.d.ts +6 -1
  64. package/dist-client/pages/kpi-history/kpi-history-list-page.js +11 -11
  65. package/dist-client/pages/kpi-history/kpi-history-list-page.js.map +1 -1
  66. package/dist-client/pages/kpi-metric/kpi-metric-list-page.d.ts +5 -0
  67. package/dist-client/pages/kpi-metric/kpi-metric-list-page.js +10 -2
  68. package/dist-client/pages/kpi-metric/kpi-metric-list-page.js.map +1 -1
  69. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.d.ts +1 -1
  70. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js +8 -8
  71. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js.map +1 -1
  72. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js +2 -2
  73. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js.map +1 -1
  74. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.d.ts +5 -0
  75. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js +16 -8
  76. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js.map +1 -1
  77. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.d.ts +1 -1
  78. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js +6 -6
  79. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js.map +1 -1
  80. package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.js +1 -2
  81. package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.js.map +1 -1
  82. package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.d.ts +5 -0
  83. package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.js +10 -2
  84. package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.js.map +1 -1
  85. package/dist-client/pages/kpi-value/kpi-value-editor-page.d.ts +2 -1
  86. package/dist-client/pages/kpi-value/kpi-value-editor-page.js +16 -8
  87. package/dist-client/pages/kpi-value/kpi-value-editor-page.js.map +1 -1
  88. package/dist-client/pages/kpi-value/kpi-value-list-page.d.ts +5 -0
  89. package/dist-client/pages/kpi-value/kpi-value-list-page.js +31 -7
  90. package/dist-client/pages/kpi-value/kpi-value-list-page.js.map +1 -1
  91. package/dist-client/route.d.ts +1 -1
  92. package/dist-client/route.js +2 -8
  93. package/dist-client/route.js.map +1 -1
  94. package/dist-client/tsconfig.tsbuildinfo +1 -1
  95. package/dist-server/controllers/kpi-metric-value-provider.d.ts +1 -1
  96. package/dist-server/controllers/kpi-metric-value-provider.js +4 -4
  97. package/dist-server/controllers/kpi-metric-value-provider.js.map +1 -1
  98. package/dist-server/controllers/kpi-value-provider.d.ts +1 -1
  99. package/dist-server/controllers/kpi-value-provider.js +3 -3
  100. package/dist-server/controllers/kpi-value-provider.js.map +1 -1
  101. package/dist-server/migrations/1752190849680-seed-kpi-metrics.d.ts +6 -0
  102. package/dist-server/migrations/1752190849680-seed-kpi-metrics.js +101 -0
  103. package/dist-server/migrations/1752190849680-seed-kpi-metrics.js.map +1 -0
  104. package/dist-server/migrations/1752190849681-seed-kpi.d.ts +5 -0
  105. package/dist-server/migrations/1752190849681-seed-kpi.js +315 -0
  106. package/dist-server/migrations/1752190849681-seed-kpi.js.map +1 -0
  107. package/dist-server/migrations/1752192090123-add-grades-to-kpi.d.ts +7 -0
  108. package/dist-server/migrations/1752192090123-add-grades-to-kpi.js +51 -0
  109. package/dist-server/migrations/1752192090123-add-grades-to-kpi.js.map +1 -0
  110. package/dist-server/migrations/1752192090124-add-kpi-statistics.d.ts +5 -0
  111. package/dist-server/migrations/1752192090124-add-kpi-statistics.js +710 -0
  112. package/dist-server/migrations/1752192090124-add-kpi-statistics.js.map +1 -0
  113. package/dist-server/migrations/1752192090128-seed-kpi-org-scope.d.ts +6 -0
  114. package/dist-server/migrations/1752192090128-seed-kpi-org-scope.js +111 -0
  115. package/dist-server/migrations/1752192090128-seed-kpi-org-scope.js.map +1 -0
  116. package/dist-server/migrations/1752192090129-seed-kpi-values.d.ts +6 -0
  117. package/dist-server/migrations/1752192090129-seed-kpi-values.js +187 -0
  118. package/dist-server/migrations/1752192090129-seed-kpi-values.js.map +1 -0
  119. package/dist-server/migrations/grade-data/x11-performance-table.json +962 -0
  120. package/dist-server/migrations/grade-data/x12-performance-table.json +611 -0
  121. package/dist-server/migrations/grade-data/x14-performance-table.json +42 -0
  122. package/dist-server/migrations/grade-data/x21-performance-table.json +889 -0
  123. package/dist-server/migrations/grade-data/x22-performance-table.json +1064 -0
  124. package/dist-server/migrations/grade-data/x23-performance-table.json +42 -0
  125. package/dist-server/migrations/grade-data/x31-performance-table.json +644 -0
  126. package/dist-server/migrations/grade-data/x32-performance-table.json +993 -0
  127. package/dist-server/migrations/grade-data/x33-performance-table.json +195 -0
  128. package/dist-server/migrations/grade-data/x34-performance-table.json +12 -0
  129. package/dist-server/migrations/grade-data/x35-performance-table.json +42 -0
  130. package/dist-server/migrations/grade-data/x41-performance-table.json +825 -0
  131. package/dist-server/migrations/grade-data/x42-performance-table.json +786 -0
  132. package/dist-server/migrations/grade-data/x43-performance-table.json +12 -0
  133. package/dist-server/migrations/grade-data/x44-performance-table.json +42 -0
  134. package/dist-server/migrations/grade-data/x51-performance-table.json +924 -0
  135. package/dist-server/migrations/grade-data/x52-performance-table.json +42 -0
  136. package/dist-server/migrations/grade-data/x61-performance-table.json +261 -0
  137. package/dist-server/migrations/grade-data/x62-performance-table.json +42 -0
  138. package/dist-server/migrations/seed-data/kpi-metrics-seed.json +454 -0
  139. package/dist-server/migrations/seed-data/kpi-org-scope-seed.json +1676 -0
  140. package/dist-server/migrations/seed-data/kpi-scopes-seed.json +121 -0
  141. package/dist-server/migrations/seed-data/kpi-values-seed.json +402 -0
  142. package/dist-server/migrations/seed-data/kpis-seed.json +488 -0
  143. package/dist-server/migrations/seed-data/scope-definitions-seed.json +90 -0
  144. package/dist-server/service/index.d.ts +4 -7
  145. package/dist-server/service/index.js +10 -13
  146. package/dist-server/service/index.js.map +1 -1
  147. package/dist-server/service/kpi/aggregate-kpi.js +30 -13
  148. package/dist-server/service/kpi/aggregate-kpi.js.map +1 -1
  149. package/dist-server/service/kpi/kpi-formula.service.d.ts +15 -0
  150. package/dist-server/service/kpi/kpi-formula.service.js +90 -0
  151. package/dist-server/service/kpi/kpi-formula.service.js.map +1 -1
  152. package/dist-server/service/kpi/kpi-history.d.ts +0 -3
  153. package/dist-server/service/kpi/kpi-history.js +0 -10
  154. package/dist-server/service/kpi/kpi-history.js.map +1 -1
  155. package/dist-server/service/kpi/kpi-mutation.d.ts +1 -1
  156. package/dist-server/service/kpi/kpi-mutation.js +57 -20
  157. package/dist-server/service/kpi/kpi-mutation.js.map +1 -1
  158. package/dist-server/service/kpi/kpi-query.d.ts +7 -3
  159. package/dist-server/service/kpi/kpi-query.js +126 -10
  160. package/dist-server/service/kpi/kpi-query.js.map +1 -1
  161. package/dist-server/service/kpi/kpi-type.d.ts +4 -2
  162. package/dist-server/service/kpi/kpi-type.js +12 -4
  163. package/dist-server/service/kpi/kpi-type.js.map +1 -1
  164. package/dist-server/service/kpi/kpi.d.ts +4 -3
  165. package/dist-server/service/kpi/kpi.js +20 -8
  166. package/dist-server/service/kpi/kpi.js.map +1 -1
  167. package/dist-server/service/kpi-metric/aggregate-kpi-metric.js +46 -11
  168. package/dist-server/service/kpi-metric/aggregate-kpi-metric.js.map +1 -1
  169. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.d.ts +1 -1
  170. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js +6 -6
  171. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js.map +1 -1
  172. package/dist-server/service/kpi-metric-value/kpi-metric-value-type.d.ts +2 -2
  173. package/dist-server/service/kpi-metric-value/kpi-metric-value-type.js +4 -4
  174. package/dist-server/service/kpi-metric-value/kpi-metric-value-type.js.map +1 -1
  175. package/dist-server/service/kpi-metric-value/kpi-metric-value.d.ts +1 -1
  176. package/dist-server/service/kpi-metric-value/kpi-metric-value.js +3 -3
  177. package/dist-server/service/kpi-metric-value/kpi-metric-value.js.map +1 -1
  178. package/dist-server/service/kpi-org-scope/index.d.ts +5 -0
  179. package/dist-server/service/kpi-org-scope/index.js +9 -0
  180. package/dist-server/service/kpi-org-scope/index.js.map +1 -0
  181. package/dist-server/service/kpi-org-scope/kpi-org-scope-mutation.d.ts +8 -0
  182. package/dist-server/service/kpi-org-scope/kpi-org-scope-mutation.js +170 -0
  183. package/dist-server/service/kpi-org-scope/kpi-org-scope-mutation.js.map +1 -0
  184. package/dist-server/service/kpi-org-scope/kpi-org-scope-query.d.ts +14 -0
  185. package/dist-server/service/kpi-org-scope/kpi-org-scope-query.js +152 -0
  186. package/dist-server/service/kpi-org-scope/kpi-org-scope-query.js.map +1 -0
  187. package/dist-server/service/kpi-org-scope/kpi-org-scope-type.d.ts +26 -0
  188. package/dist-server/service/kpi-org-scope/kpi-org-scope-type.js +101 -0
  189. package/dist-server/service/kpi-org-scope/kpi-org-scope-type.js.map +1 -0
  190. package/dist-server/service/kpi-org-scope/kpi-org-scope.d.ts +26 -0
  191. package/dist-server/service/kpi-org-scope/kpi-org-scope.js +135 -0
  192. package/dist-server/service/kpi-org-scope/kpi-org-scope.js.map +1 -0
  193. package/dist-server/service/kpi-scope/index.d.ts +9 -0
  194. package/dist-server/service/kpi-scope/index.js +14 -0
  195. package/dist-server/service/kpi-scope/index.js.map +1 -0
  196. package/dist-server/service/kpi-scope/kpi-scope-mutation.d.ts +9 -0
  197. package/dist-server/service/kpi-scope/kpi-scope-mutation.js +135 -0
  198. package/dist-server/service/kpi-scope/kpi-scope-mutation.js.map +1 -0
  199. package/dist-server/service/kpi-scope/kpi-scope-query.d.ts +11 -0
  200. package/dist-server/service/kpi-scope/kpi-scope-query.js +89 -0
  201. package/dist-server/service/kpi-scope/kpi-scope-query.js.map +1 -0
  202. package/dist-server/service/kpi-scope/kpi-scope-type.d.ts +35 -0
  203. package/dist-server/service/kpi-scope/kpi-scope-type.js +138 -0
  204. package/dist-server/service/kpi-scope/kpi-scope-type.js.map +1 -0
  205. package/dist-server/service/kpi-scope/kpi-scope.d.ts +38 -0
  206. package/dist-server/service/kpi-scope/kpi-scope.js +144 -0
  207. package/dist-server/service/kpi-scope/kpi-scope.js.map +1 -0
  208. package/dist-server/service/kpi-statistic/kpi-statistic-batch.service.d.ts +43 -0
  209. package/dist-server/service/kpi-statistic/kpi-statistic-batch.service.js +181 -0
  210. package/dist-server/service/kpi-statistic/kpi-statistic-batch.service.js.map +1 -0
  211. package/dist-server/service/kpi-statistic/kpi-statistic-calculation.service.d.ts +50 -0
  212. package/dist-server/service/kpi-statistic/kpi-statistic-calculation.service.js +324 -0
  213. package/dist-server/service/kpi-statistic/kpi-statistic-calculation.service.js.map +1 -0
  214. package/dist-server/service/kpi-statistic/kpi-statistic-mutation.d.ts +4 -0
  215. package/dist-server/service/kpi-statistic/kpi-statistic-mutation.js +76 -0
  216. package/dist-server/service/kpi-statistic/kpi-statistic-mutation.js.map +1 -1
  217. package/dist-server/service/kpi-statistic/kpi-statistic-query.d.ts +5 -1
  218. package/dist-server/service/kpi-statistic/kpi-statistic-query.js +92 -1
  219. package/dist-server/service/kpi-statistic/kpi-statistic-query.js.map +1 -1
  220. package/dist-server/service/kpi-statistic/kpi-statistic.d.ts +4 -0
  221. package/dist-server/service/kpi-statistic/kpi-statistic.js +33 -0
  222. package/dist-server/service/kpi-statistic/kpi-statistic.js.map +1 -1
  223. package/dist-server/service/kpi-value/kpi-value-mutation.js +71 -7
  224. package/dist-server/service/kpi-value/kpi-value-mutation.js.map +1 -1
  225. package/dist-server/service/kpi-value/kpi-value-type.d.ts +4 -2
  226. package/dist-server/service/kpi-value/kpi-value-type.js +12 -4
  227. package/dist-server/service/kpi-value/kpi-value-type.js.map +1 -1
  228. package/dist-server/service/kpi-value/kpi-value.d.ts +3 -1
  229. package/dist-server/service/kpi-value/kpi-value.js +11 -5
  230. package/dist-server/service/kpi-value/kpi-value.js.map +1 -1
  231. package/dist-server/service/utils/value-date-util.d.ts +1 -0
  232. package/dist-server/service/utils/value-date-util.js +41 -0
  233. package/dist-server/service/utils/value-date-util.js.map +1 -1
  234. package/dist-server/tsconfig.json +10 -0
  235. package/dist-server/tsconfig.tsbuildinfo +1 -1
  236. package/package.json +7 -6
  237. package/server/@types/index.d.ts +11 -0
  238. package/server/controllers/kpi-metric-value-provider.ts +5 -5
  239. package/server/controllers/kpi-value-provider.ts +4 -4
  240. package/server/migrations/1752190849680-seed-kpi-metrics.ts +124 -0
  241. package/server/migrations/1752190849681-seed-kpi.ts +356 -0
  242. package/server/migrations/1752192090123-add-grades-to-kpi.ts +67 -0
  243. package/server/migrations/1752192090124-add-kpi-statistics.ts +719 -0
  244. package/server/migrations/1752192090128-seed-kpi-org-scope.ts +132 -0
  245. package/server/migrations/1752192090129-seed-kpi-values.ts +207 -0
  246. package/server/migrations/grade-data/x11-performance-table.json +962 -0
  247. package/server/migrations/grade-data/x12-performance-table.json +611 -0
  248. package/server/migrations/grade-data/x14-performance-table.json +42 -0
  249. package/server/migrations/grade-data/x21-performance-table.json +889 -0
  250. package/server/migrations/grade-data/x22-performance-table.json +1064 -0
  251. package/server/migrations/grade-data/x23-performance-table.json +42 -0
  252. package/server/migrations/grade-data/x31-performance-table.json +644 -0
  253. package/server/migrations/grade-data/x32-performance-table.json +993 -0
  254. package/server/migrations/grade-data/x33-performance-table.json +195 -0
  255. package/server/migrations/grade-data/x34-performance-table.json +12 -0
  256. package/server/migrations/grade-data/x35-performance-table.json +42 -0
  257. package/server/migrations/grade-data/x41-performance-table.json +825 -0
  258. package/server/migrations/grade-data/x42-performance-table.json +786 -0
  259. package/server/migrations/grade-data/x43-performance-table.json +12 -0
  260. package/server/migrations/grade-data/x44-performance-table.json +42 -0
  261. package/server/migrations/grade-data/x51-performance-table.json +924 -0
  262. package/server/migrations/grade-data/x52-performance-table.json +42 -0
  263. package/server/migrations/grade-data/x61-performance-table.json +261 -0
  264. package/server/migrations/grade-data/x62-performance-table.json +42 -0
  265. package/server/migrations/seed-data/kpi-metrics-seed.json +454 -0
  266. package/server/migrations/seed-data/kpi-org-scope-seed.json +1676 -0
  267. package/server/migrations/seed-data/kpi-scopes-seed.json +121 -0
  268. package/server/migrations/seed-data/kpi-values-seed.json +402 -0
  269. package/server/migrations/seed-data/kpis-seed.json +488 -0
  270. package/server/migrations/seed-data/scope-definitions-seed.json +90 -0
  271. package/server/service/index.ts +10 -13
  272. package/server/service/kpi/aggregate-kpi.ts +31 -13
  273. package/server/service/kpi/kpi-formula.service.ts +101 -0
  274. package/server/service/kpi/kpi-history.ts +0 -8
  275. package/server/service/kpi/kpi-mutation.ts +59 -19
  276. package/server/service/kpi/kpi-query.ts +119 -8
  277. package/server/service/kpi/kpi-type.ts +10 -4
  278. package/server/service/kpi/kpi.ts +17 -7
  279. package/server/service/kpi-metric/aggregate-kpi-metric.ts +55 -11
  280. package/server/service/kpi-metric-value/kpi-metric-value-mutation.ts +6 -6
  281. package/server/service/kpi-metric-value/kpi-metric-value-type.ts +4 -4
  282. package/server/service/kpi-metric-value/kpi-metric-value.ts +3 -3
  283. package/server/service/kpi-org-scope/index.ts +6 -0
  284. package/server/service/kpi-org-scope/kpi-org-scope-mutation.ts +173 -0
  285. package/server/service/kpi-org-scope/kpi-org-scope-query.ts +127 -0
  286. package/server/service/kpi-org-scope/kpi-org-scope-type.ts +68 -0
  287. package/server/service/kpi-org-scope/kpi-org-scope.ts +123 -0
  288. package/server/service/kpi-scope/index.ts +11 -0
  289. package/server/service/kpi-scope/kpi-scope-mutation.ts +129 -0
  290. package/server/service/kpi-scope/kpi-scope-query.ts +63 -0
  291. package/server/service/kpi-scope/kpi-scope-type.ts +96 -0
  292. package/server/service/kpi-scope/kpi-scope.ts +143 -0
  293. package/server/service/kpi-statistic/kpi-statistic-batch.service.ts +231 -0
  294. package/server/service/kpi-statistic/kpi-statistic-calculation.service.ts +410 -0
  295. package/server/service/kpi-statistic/kpi-statistic-mutation.ts +97 -0
  296. package/server/service/kpi-statistic/kpi-statistic-query.ts +89 -2
  297. package/server/service/kpi-statistic/kpi-statistic.ts +32 -0
  298. package/server/service/kpi-value/kpi-value-mutation.ts +73 -7
  299. package/server/service/kpi-value/kpi-value-type.ts +10 -4
  300. package/server/service/kpi-value/kpi-value.ts +10 -5
  301. package/server/service/utils/value-date-util.ts +47 -0
  302. package/server/types/global.d.ts +8 -0
  303. package/things-factory.config.js +1 -0
  304. package/translations/en.json +15 -3
  305. package/translations/ja.json +13 -3
  306. package/translations/ko.json +15 -3
  307. package/translations/ms.json +13 -3
  308. package/translations/zh.json +13 -3
  309. package/client/pages/kpi-category/kpi-category-importer.ts +0 -90
  310. package/client/pages/kpi-category/kpi-category-list-page.ts +0 -537
  311. package/client/pages/kpi-category/kpi-category-value-calculator.ts +0 -233
  312. package/client/pages/kpi-category-value/kpi-category-value-list-page.ts +0 -404
  313. package/dist-client/pages/kpi-category/kpi-category-importer.d.ts +0 -23
  314. package/dist-client/pages/kpi-category/kpi-category-importer.js +0 -92
  315. package/dist-client/pages/kpi-category/kpi-category-importer.js.map +0 -1
  316. package/dist-client/pages/kpi-category/kpi-category-list-page.d.ts +0 -74
  317. package/dist-client/pages/kpi-category/kpi-category-list-page.js +0 -517
  318. package/dist-client/pages/kpi-category/kpi-category-list-page.js.map +0 -1
  319. package/dist-client/pages/kpi-category/kpi-category-value-calculator.d.ts +0 -13
  320. package/dist-client/pages/kpi-category/kpi-category-value-calculator.js +0 -256
  321. package/dist-client/pages/kpi-category/kpi-category-value-calculator.js.map +0 -1
  322. package/dist-client/pages/kpi-category-value/kpi-category-value-list-page.d.ts +0 -63
  323. package/dist-client/pages/kpi-category-value/kpi-category-value-list-page.js +0 -393
  324. package/dist-client/pages/kpi-category-value/kpi-category-value-list-page.js.map +0 -1
  325. package/dist-server/service/kpi-category/index.d.ts +0 -6
  326. package/dist-server/service/kpi-category/index.js +0 -10
  327. package/dist-server/service/kpi-category/index.js.map +0 -1
  328. package/dist-server/service/kpi-category/kpi-category-mutation.d.ts +0 -9
  329. package/dist-server/service/kpi-category/kpi-category-mutation.js +0 -221
  330. package/dist-server/service/kpi-category/kpi-category-mutation.js.map +0 -1
  331. package/dist-server/service/kpi-category/kpi-category-query.d.ts +0 -18
  332. package/dist-server/service/kpi-category/kpi-category-query.js +0 -115
  333. package/dist-server/service/kpi-category/kpi-category-query.js.map +0 -1
  334. package/dist-server/service/kpi-category/kpi-category-type.d.ts +0 -24
  335. package/dist-server/service/kpi-category/kpi-category-type.js +0 -100
  336. package/dist-server/service/kpi-category/kpi-category-type.js.map +0 -1
  337. package/dist-server/service/kpi-category/kpi-category.d.ts +0 -22
  338. package/dist-server/service/kpi-category/kpi-category.js +0 -106
  339. package/dist-server/service/kpi-category/kpi-category.js.map +0 -1
  340. package/dist-server/service/kpi-category-value/index.d.ts +0 -6
  341. package/dist-server/service/kpi-category-value/index.js +0 -10
  342. package/dist-server/service/kpi-category-value/index.js.map +0 -1
  343. package/dist-server/service/kpi-category-value/kpi-category-value-mutation.d.ts +0 -8
  344. package/dist-server/service/kpi-category-value/kpi-category-value-mutation.js +0 -102
  345. package/dist-server/service/kpi-category-value/kpi-category-value-mutation.js.map +0 -1
  346. package/dist-server/service/kpi-category-value/kpi-category-value-query.d.ts +0 -13
  347. package/dist-server/service/kpi-category-value/kpi-category-value-query.js +0 -91
  348. package/dist-server/service/kpi-category-value/kpi-category-value-query.js.map +0 -1
  349. package/dist-server/service/kpi-category-value/kpi-category-value-type.d.ts +0 -19
  350. package/dist-server/service/kpi-category-value/kpi-category-value-type.js +0 -73
  351. package/dist-server/service/kpi-category-value/kpi-category-value-type.js.map +0 -1
  352. package/dist-server/service/kpi-category-value/kpi-category-value.d.ts +0 -19
  353. package/dist-server/service/kpi-category-value/kpi-category-value.js +0 -91
  354. package/dist-server/service/kpi-category-value/kpi-category-value.js.map +0 -1
  355. package/helps/kpi/kpi-category.md +0 -160
  356. package/server/service/kpi-category/index.ts +0 -7
  357. package/server/service/kpi-category/kpi-category-mutation.ts +0 -217
  358. package/server/service/kpi-category/kpi-category-query.ts +0 -87
  359. package/server/service/kpi-category/kpi-category-type.ts +0 -73
  360. package/server/service/kpi-category/kpi-category.ts +0 -95
  361. package/server/service/kpi-category-value/index.ts +0 -7
  362. package/server/service/kpi-category-value/kpi-category-value-mutation.ts +0 -88
  363. package/server/service/kpi-category-value/kpi-category-value-query.ts +0 -62
  364. package/server/service/kpi-category-value/kpi-category-value-type.ts +0 -48
  365. package/server/service/kpi-category-value/kpi-category-value.ts +0 -79
@@ -6,7 +6,7 @@ let KpiBoxplotChart = class KpiBoxplotChart extends LitElement {
6
6
  constructor() {
7
7
  super(...arguments);
8
8
  this.data = [];
9
- this.groups = [];
9
+ this.groups = []; // 조직 단위 목록 (빈 배열이면 데이터에서 자동 추출)
10
10
  this.minKey = 'min';
11
11
  this.maxKey = 'max';
12
12
  this.meanKey = 'mean';
@@ -15,8 +15,10 @@ let KpiBoxplotChart = class KpiBoxplotChart extends LitElement {
15
15
  this.q3Key = 'q3';
16
16
  this.valueKey = 'value';
17
17
  this.currentGroup = '';
18
+ this.independentScale = false;
18
19
  this.chartWidth = 0;
19
20
  this.chartHeight = 0;
21
+ this.getOrgValue = (d) => d.org || d.group;
20
22
  }
21
23
  static { this.styles = css `
22
24
  :host {
@@ -61,94 +63,214 @@ let KpiBoxplotChart = class KpiBoxplotChart extends LitElement {
61
63
  drawBoxplot() {
62
64
  const svg = d3.select(this.renderRoot.querySelector('#boxplot'));
63
65
  svg.selectAll('*').remove();
66
+ // 데이터 검증
67
+ if (!this.data || this.data.length === 0) {
68
+ return;
69
+ }
64
70
  const w = this.chartWidth || 300;
65
71
  const h = this.chartHeight || 300;
66
- const margin = { top: 20, right: 20, bottom: 40, left: 40 };
72
+ const margin = {
73
+ top: 20,
74
+ right: 20,
75
+ bottom: 40,
76
+ left: this.independentScale ? 20 : 40
77
+ };
67
78
  const plotW = w - margin.left - margin.right;
68
79
  const plotH = h - margin.top - margin.bottom;
69
- // x축: 그룹
70
- const x = d3.scaleBand().domain(this.groups).range([0, plotW]).padding(0.4);
71
- // y축:
72
- const allValues = this.data.flatMap(d => [
73
- d[this.minKey],
74
- d[this.maxKey],
75
- d[this.q1Key],
76
- d[this.q3Key],
77
- d[this.medianKey],
78
- d[this.meanKey],
79
- d[this.valueKey]
80
- ]);
81
- const y = d3
82
- .scaleLinear()
83
- .domain([d3.min(allValues) ?? 0, d3.max(allValues) ?? 1])
84
- .nice()
85
- .range([plotH, 0]);
80
+ // x축: 조직 단위 (그룹) - org 또는 group 필드 지원
81
+ const groups = this.groups.length > 0 ? this.groups : [...new Set(this.data.map(d => this.getOrgValue(d)).filter(org => org != null))];
82
+ if (groups.length === 0) {
83
+ console.warn('No valid groups found in data:', this.data);
84
+ return; // 유효한 그룹이 없으면 차트를 그리지 않음
85
+ }
86
+ console.log('Boxplot groups:', groups);
87
+ console.log('Data org values:', this.data.map(d => this.getOrgValue(d)));
88
+ console.log('Full data:', this.data);
89
+ console.log('Chart dimensions:', { w, h, plotW, plotH });
90
+ const x = d3.scaleBand().domain(groups).range([0, plotW]).padding(0.4);
91
+ // y축 스케일 설정
92
+ let y;
93
+ if (this.independentScale) {
94
+ // 독립 스케일: 시리즈별로 개별 스케일 생성
95
+ const yScales = this.data.map(d => {
96
+ const values = [
97
+ d[this.minKey],
98
+ d[this.maxKey],
99
+ d[this.q1Key],
100
+ d[this.q3Key],
101
+ d[this.medianKey],
102
+ d[this.meanKey],
103
+ d[this.valueKey]
104
+ ];
105
+ const min = d3.min(values) ?? 0;
106
+ const max = d3.max(values) ?? 1;
107
+ return {
108
+ org: d.org,
109
+ scale: d3.scaleLinear().domain([min, max]).nice().range([plotH, 0])
110
+ };
111
+ });
112
+ // 기본 y축은 첫 번째 스케일 사용
113
+ y = yScales[0]?.scale || d3.scaleLinear().domain([0, 1]).range([plotH, 0]);
114
+ }
115
+ else {
116
+ // 통합 스케일: 모든 데이터를 하나의 스케일에 맞춤
117
+ const allValues = this.data.flatMap(d => [
118
+ d[this.minKey],
119
+ d[this.maxKey],
120
+ d[this.q1Key],
121
+ d[this.q3Key],
122
+ d[this.medianKey],
123
+ d[this.meanKey],
124
+ d[this.valueKey]
125
+ ]);
126
+ y = d3
127
+ .scaleLinear()
128
+ .domain([d3.min(allValues) ?? 0, d3.max(allValues) ?? 1])
129
+ .nice()
130
+ .range([plotH, 0]);
131
+ }
86
132
  const g = svg
87
133
  .attr('width', w)
88
134
  .attr('height', h)
89
135
  .append('g')
90
136
  .attr('transform', `translate(${margin.left},${margin.top})`);
91
137
  // 축
92
- g.append('g').call(d3.axisLeft(y));
138
+ if (!this.independentScale) {
139
+ g.append('g').call(d3.axisLeft(y));
140
+ }
93
141
  g.append('g').attr('transform', `translate(0,${plotH})`).call(d3.axisBottom(x));
94
- // 박스플롯
95
- this.data.forEach(d => {
96
- const gx = x(d.group) ?? 0;
142
+ // 박스플롯 - 각 그룹별로 박스 생성
143
+ groups.forEach(groupName => {
144
+ // 해당 그룹의 데이터 찾기
145
+ const groupData = this.data.find(d => this.getOrgValue(d) === groupName);
146
+ console.log(`Searching for group: ${groupName}, found:`, groupData);
147
+ if (!groupData) {
148
+ console.warn(`No data found for group: ${groupName}. Available data:`, this.data.map(d => ({ org: d.org, keys: Object.keys(d) })));
149
+ return;
150
+ }
151
+ // 필수 필드 검증
152
+ const requiredFields = [this.minKey, this.maxKey, this.q1Key, this.q3Key, this.medianKey, this.meanKey];
153
+ const missingFields = requiredFields.filter(field => groupData[field] == null);
154
+ if (missingFields.length > 0) {
155
+ console.warn(`Missing required fields for group ${groupName}:`, missingFields);
156
+ console.log('Group data:', groupData);
157
+ return;
158
+ }
159
+ const gx = x(groupName) ?? 0;
160
+ console.log(`Drawing box for ${groupName} at x=${gx}, bandwidth=${x.bandwidth()}`);
161
+ // 독립 스케일 사용 시 해당 그룹의 스케일 찾기
162
+ let currentY = y;
163
+ if (this.independentScale) {
164
+ const values = [
165
+ groupData[this.minKey],
166
+ groupData[this.maxKey],
167
+ groupData[this.q1Key],
168
+ groupData[this.q3Key],
169
+ groupData[this.medianKey],
170
+ groupData[this.meanKey],
171
+ groupData[this.valueKey]
172
+ ];
173
+ const min = d3.min(values) ?? 0;
174
+ const max = d3.max(values) ?? 1;
175
+ currentY = d3.scaleLinear().domain([min, max]).nice().range([plotH, 0]);
176
+ }
177
+ // Outlier 계산 (1.5 * IQR 규칙)
178
+ const iqr = groupData[this.q3Key] - groupData[this.q1Key];
179
+ const lowerFence = groupData[this.q1Key] - 1.5 * iqr;
180
+ const upperFence = groupData[this.q3Key] + 1.5 * iqr;
181
+ // 실제 min/max 값 (fence 내부)
182
+ const actualMin = Math.max(groupData[this.minKey], lowerFence);
183
+ const actualMax = Math.min(groupData[this.maxKey], upperFence);
97
184
  // 박스
98
185
  g.append('rect')
99
186
  .attr('x', gx)
100
- .attr('y', y(d[this.q3Key]))
187
+ .attr('y', currentY(groupData[this.q3Key]))
101
188
  .attr('width', x.bandwidth())
102
- .attr('height', y(d[this.q1Key]) - y(d[this.q3Key]))
103
- .attr('fill', d.group === this.currentGroup ? '#2196f3' : '#bbb')
189
+ .attr('height', currentY(groupData[this.q1Key]) - currentY(groupData[this.q3Key]))
190
+ .attr('fill', this.getOrgValue(groupData) === this.currentGroup ? '#2196f3' : '#bbb')
104
191
  .attr('opacity', 0.5);
105
192
  // 중앙선(중앙값)
106
193
  g.append('line')
107
194
  .attr('x1', gx)
108
195
  .attr('x2', gx + x.bandwidth())
109
- .attr('y1', y(d[this.medianKey]))
110
- .attr('y2', y(d[this.medianKey]))
196
+ .attr('y1', currentY(groupData[this.medianKey]))
197
+ .attr('y2', currentY(groupData[this.medianKey]))
111
198
  .attr('stroke', '#333')
112
199
  .attr('stroke-width', 2);
113
- // 수염(min-max)
200
+ // 수염 (fence 내부의 min-max)
114
201
  g.append('line')
115
202
  .attr('x1', gx + x.bandwidth() / 2)
116
203
  .attr('x2', gx + x.bandwidth() / 2)
117
- .attr('y1', y(d[this.minKey]))
118
- .attr('y2', y(d[this.maxKey]))
204
+ .attr('y1', currentY(actualMin))
205
+ .attr('y2', currentY(actualMax))
119
206
  .attr('stroke', '#333');
120
- // min/max
207
+ // min/max 선 (fence 내부)
121
208
  g.append('line')
122
209
  .attr('x1', gx + x.bandwidth() / 4)
123
210
  .attr('x2', gx + (x.bandwidth() * 3) / 4)
124
- .attr('y1', y(d[this.minKey]))
125
- .attr('y2', y(d[this.minKey]))
211
+ .attr('y1', currentY(actualMin))
212
+ .attr('y2', currentY(actualMin))
126
213
  .attr('stroke', '#333');
127
214
  g.append('line')
128
215
  .attr('x1', gx + x.bandwidth() / 4)
129
216
  .attr('x2', gx + (x.bandwidth() * 3) / 4)
130
- .attr('y1', y(d[this.maxKey]))
131
- .attr('y2', y(d[this.maxKey]))
217
+ .attr('y1', currentY(actualMax))
218
+ .attr('y2', currentY(actualMax))
132
219
  .attr('stroke', '#333');
220
+ // Outlier 표시 (fence 외부의 값들)
221
+ if (groupData[this.minKey] < lowerFence) {
222
+ g.append('circle')
223
+ .attr('cx', gx + x.bandwidth() / 2)
224
+ .attr('cy', currentY(groupData[this.minKey]))
225
+ .attr('r', 3)
226
+ .attr('fill', '#ff4444')
227
+ .attr('stroke', '#333')
228
+ .attr('stroke-width', 1);
229
+ }
230
+ if (groupData[this.maxKey] > upperFence) {
231
+ g.append('circle')
232
+ .attr('cx', gx + x.bandwidth() / 2)
233
+ .attr('cy', currentY(groupData[this.maxKey]))
234
+ .attr('r', 3)
235
+ .attr('fill', '#ff4444')
236
+ .attr('stroke', '#333')
237
+ .attr('stroke-width', 1);
238
+ }
133
239
  // 평균값
134
240
  g.append('circle')
135
241
  .attr('cx', gx + x.bandwidth() / 2)
136
- .attr('cy', y(d[this.meanKey]))
242
+ .attr('cy', currentY(groupData[this.meanKey]))
137
243
  .attr('r', 4)
138
244
  .attr('fill', 'orange');
139
245
  });
140
246
  // 현재 그룹 값 강조
141
- this.data.forEach(d => {
142
- if (d.group === this.currentGroup) {
247
+ if (this.currentGroup && groups.includes(this.currentGroup)) {
248
+ const currentGroupData = this.data.find(d => this.getOrgValue(d) === this.currentGroup);
249
+ if (currentGroupData) {
250
+ let currentY = y;
251
+ if (this.independentScale) {
252
+ const values = [
253
+ currentGroupData[this.minKey],
254
+ currentGroupData[this.maxKey],
255
+ currentGroupData[this.q1Key],
256
+ currentGroupData[this.q3Key],
257
+ currentGroupData[this.medianKey],
258
+ currentGroupData[this.meanKey],
259
+ currentGroupData[this.valueKey]
260
+ ];
261
+ const min = d3.min(values) ?? 0;
262
+ const max = d3.max(values) ?? 1;
263
+ currentY = d3.scaleLinear().domain([min, max]).nice().range([plotH, 0]);
264
+ }
143
265
  g.append('circle')
144
- .attr('cx', x(d.group) + x.bandwidth() / 2)
145
- .attr('cy', y(d[this.valueKey]))
266
+ .attr('cx', x(this.currentGroup) + x.bandwidth() / 2)
267
+ .attr('cy', currentY(currentGroupData[this.valueKey]))
146
268
  .attr('r', 6)
147
269
  .attr('fill', '#e91e63')
148
270
  .attr('stroke', '#fff')
149
271
  .attr('stroke-width', 2);
150
272
  }
151
- });
273
+ }
152
274
  }
153
275
  };
154
276
  __decorate([
@@ -191,6 +313,10 @@ __decorate([
191
313
  property({ type: String }),
192
314
  __metadata("design:type", String)
193
315
  ], KpiBoxplotChart.prototype, "currentGroup", void 0);
316
+ __decorate([
317
+ property({ type: Boolean }),
318
+ __metadata("design:type", Boolean)
319
+ ], KpiBoxplotChart.prototype, "independentScale", void 0);
194
320
  KpiBoxplotChart = __decorate([
195
321
  customElement('kpi-boxplot-chart')
196
322
  ], KpiBoxplotChart);
@@ -1 +1 @@
1
- {"version":3,"file":"kpi-boxplot-chart.js","sourceRoot":"","sources":["../../client/charts/kpi-boxplot-chart.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, html, css } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport * as d3 from 'd3'\n\n@customElement('kpi-boxplot-chart')\nexport class KpiBoxplotChart extends LitElement {\n @property({ type: Array }) data: any[] = []\n @property({ type: Array }) groups: string[] = []\n @property({ type: String }) minKey: string = 'min'\n @property({ type: String }) maxKey: string = 'max'\n @property({ type: String }) meanKey: string = 'mean'\n @property({ type: String }) medianKey: string = 'median'\n @property({ type: String }) q1Key: string = 'q1'\n @property({ type: String }) q3Key: string = 'q3'\n @property({ type: String }) valueKey: string = 'value'\n @property({ type: String }) currentGroup: string = ''\n\n static styles = css`\n :host {\n display: block;\n width: 100%;\n height: 100%;\n }\n svg {\n width: 100%;\n height: 100%;\n display: block;\n }\n `\n\n private chartWidth = 0\n private chartHeight = 0\n private resizeObserver?: ResizeObserver\n\n render() {\n return html`<svg\n id=\"boxplot\"\n width=${this.chartWidth}\n height=${this.chartHeight}\n viewBox=\"0 0 ${this.chartWidth} ${this.chartHeight}\"\n preserveAspectRatio=\"xMidYMid meet\"\n ></svg>`\n }\n\n connectedCallback() {\n super.connectedCallback()\n this.resizeObserver = new ResizeObserver(entries => {\n for (const entry of entries) {\n const rect = entry.contentRect\n this.chartWidth = rect.width\n this.chartHeight = rect.height\n this.requestUpdate()\n }\n })\n this.resizeObserver.observe(this)\n }\n\n disconnectedCallback() {\n this.resizeObserver?.disconnect()\n super.disconnectedCallback()\n }\n\n updated() {\n this.drawBoxplot()\n }\n\n drawBoxplot() {\n const svg = d3.select(this.renderRoot.querySelector('#boxplot'))\n svg.selectAll('*').remove()\n const w = this.chartWidth || 300\n const h = this.chartHeight || 300\n const margin = { top: 20, right: 20, bottom: 40, left: 40 }\n const plotW = w - margin.left - margin.right\n const plotH = h - margin.top - margin.bottom\n\n // x축: 그룹\n const x = d3.scaleBand().domain(this.groups).range([0, plotW]).padding(0.4)\n // y축: 값\n const allValues = this.data.flatMap(d => [\n d[this.minKey],\n d[this.maxKey],\n d[this.q1Key],\n d[this.q3Key],\n d[this.medianKey],\n d[this.meanKey],\n d[this.valueKey]\n ])\n const y = d3\n .scaleLinear()\n .domain([d3.min(allValues) ?? 0, d3.max(allValues) ?? 1])\n .nice()\n .range([plotH, 0])\n\n const g = svg\n .attr('width', w)\n .attr('height', h)\n .append('g')\n .attr('transform', `translate(${margin.left},${margin.top})`)\n\n // 축\n g.append('g').call(d3.axisLeft(y))\n g.append('g').attr('transform', `translate(0,${plotH})`).call(d3.axisBottom(x))\n\n // 박스플롯\n this.data.forEach(d => {\n const gx = x(d.group) ?? 0\n // 박스\n g.append('rect')\n .attr('x', gx)\n .attr('y', y(d[this.q3Key]))\n .attr('width', x.bandwidth())\n .attr('height', y(d[this.q1Key]) - y(d[this.q3Key]))\n .attr('fill', d.group === this.currentGroup ? '#2196f3' : '#bbb')\n .attr('opacity', 0.5)\n // 중앙선(중앙값)\n g.append('line')\n .attr('x1', gx)\n .attr('x2', gx + x.bandwidth())\n .attr('y1', y(d[this.medianKey]))\n .attr('y2', y(d[this.medianKey]))\n .attr('stroke', '#333')\n .attr('stroke-width', 2)\n // 수염(min-max)\n g.append('line')\n .attr('x1', gx + x.bandwidth() / 2)\n .attr('x2', gx + x.bandwidth() / 2)\n .attr('y1', y(d[this.minKey]))\n .attr('y2', y(d[this.maxKey]))\n .attr('stroke', '#333')\n // min/max\n g.append('line')\n .attr('x1', gx + x.bandwidth() / 4)\n .attr('x2', gx + (x.bandwidth() * 3) / 4)\n .attr('y1', y(d[this.minKey]))\n .attr('y2', y(d[this.minKey]))\n .attr('stroke', '#333')\n g.append('line')\n .attr('x1', gx + x.bandwidth() / 4)\n .attr('x2', gx + (x.bandwidth() * 3) / 4)\n .attr('y1', y(d[this.maxKey]))\n .attr('y2', y(d[this.maxKey]))\n .attr('stroke', '#333')\n // 평균값\n g.append('circle')\n .attr('cx', gx + x.bandwidth() / 2)\n .attr('cy', y(d[this.meanKey]))\n .attr('r', 4)\n .attr('fill', 'orange')\n })\n // 현재 그룹 값 강조\n this.data.forEach(d => {\n if (d.group === this.currentGroup) {\n g.append('circle')\n .attr('cx', x(d.group) + x.bandwidth() / 2)\n .attr('cy', y(d[this.valueKey]))\n .attr('r', 6)\n .attr('fill', '#e91e63')\n .attr('stroke', '#fff')\n .attr('stroke-width', 2)\n }\n })\n }\n}\n"]}
1
+ {"version":3,"file":"kpi-boxplot-chart.js","sourceRoot":"","sources":["../../client/charts/kpi-boxplot-chart.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, html, css } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport * as d3 from 'd3'\n\n@customElement('kpi-boxplot-chart')\nexport class KpiBoxplotChart extends LitElement {\n @property({ type: Array }) data: any[] = []\n @property({ type: Array }) groups: string[] = [] // 조직 단위 목록 (빈 배열이면 데이터에서 자동 추출)\n @property({ type: String }) minKey: string = 'min'\n @property({ type: String }) maxKey: string = 'max'\n @property({ type: String }) meanKey: string = 'mean'\n @property({ type: String }) medianKey: string = 'median'\n @property({ type: String }) q1Key: string = 'q1'\n @property({ type: String }) q3Key: string = 'q3'\n @property({ type: String }) valueKey: string = 'value'\n @property({ type: String }) currentGroup: string = ''\n @property({ type: Boolean }) independentScale: boolean = false\n\n static styles = css`\n :host {\n display: block;\n width: 100%;\n height: 100%;\n }\n svg {\n width: 100%;\n height: 100%;\n display: block;\n }\n `\n\n private chartWidth = 0\n private chartHeight = 0\n private resizeObserver?: ResizeObserver\n\n render() {\n return html`<svg\n id=\"boxplot\"\n width=${this.chartWidth}\n height=${this.chartHeight}\n viewBox=\"0 0 ${this.chartWidth} ${this.chartHeight}\"\n preserveAspectRatio=\"xMidYMid meet\"\n ></svg>`\n }\n\n connectedCallback() {\n super.connectedCallback()\n this.resizeObserver = new ResizeObserver(entries => {\n for (const entry of entries) {\n const rect = entry.contentRect\n this.chartWidth = rect.width\n this.chartHeight = rect.height\n this.requestUpdate()\n }\n })\n this.resizeObserver.observe(this)\n }\n\n disconnectedCallback() {\n this.resizeObserver?.disconnect()\n super.disconnectedCallback()\n }\n\n updated() {\n this.drawBoxplot()\n }\n\n private getOrgValue = (d: any) => d.org || d.group\n\n drawBoxplot() {\n const svg = d3.select(this.renderRoot.querySelector('#boxplot'))\n svg.selectAll('*').remove()\n \n // 데이터 검증\n if (!this.data || this.data.length === 0) {\n return\n }\n const w = this.chartWidth || 300\n const h = this.chartHeight || 300\n const margin = {\n top: 20,\n right: 20,\n bottom: 40,\n left: this.independentScale ? 20 : 40\n }\n const plotW = w - margin.left - margin.right\n const plotH = h - margin.top - margin.bottom\n\n // x축: 조직 단위 (그룹) - org 또는 group 필드 지원\n const groups = this.groups.length > 0 ? this.groups : [...new Set(this.data.map(d => this.getOrgValue(d)).filter(org => org != null))]\n if (groups.length === 0) {\n console.warn('No valid groups found in data:', this.data)\n return // 유효한 그룹이 없으면 차트를 그리지 않음\n }\n \n console.log('Boxplot groups:', groups)\n console.log('Data org values:', this.data.map(d => this.getOrgValue(d)))\n console.log('Full data:', this.data)\n console.log('Chart dimensions:', { w, h, plotW, plotH })\n \n const x = d3.scaleBand().domain(groups).range([0, plotW]).padding(0.4)\n\n // y축 스케일 설정\n let y: d3.ScaleLinear<number, number>\n\n if (this.independentScale) {\n // 독립 스케일: 각 시리즈별로 개별 스케일 생성\n const yScales = this.data.map(d => {\n const values = [\n d[this.minKey],\n d[this.maxKey],\n d[this.q1Key],\n d[this.q3Key],\n d[this.medianKey],\n d[this.meanKey],\n d[this.valueKey]\n ]\n const min = d3.min(values) ?? 0\n const max = d3.max(values) ?? 1\n return {\n org: d.org,\n scale: d3.scaleLinear().domain([min, max]).nice().range([plotH, 0])\n }\n })\n\n // 기본 y축은 첫 번째 스케일 사용\n y = yScales[0]?.scale || d3.scaleLinear().domain([0, 1]).range([plotH, 0])\n } else {\n // 통합 스케일: 모든 데이터를 하나의 스케일에 맞춤\n const allValues = this.data.flatMap(d => [\n d[this.minKey],\n d[this.maxKey],\n d[this.q1Key],\n d[this.q3Key],\n d[this.medianKey],\n d[this.meanKey],\n d[this.valueKey]\n ])\n y = d3\n .scaleLinear()\n .domain([d3.min(allValues) ?? 0, d3.max(allValues) ?? 1])\n .nice()\n .range([plotH, 0])\n }\n\n const g = svg\n .attr('width', w)\n .attr('height', h)\n .append('g')\n .attr('transform', `translate(${margin.left},${margin.top})`)\n\n // 축\n if (!this.independentScale) {\n g.append('g').call(d3.axisLeft(y))\n }\n g.append('g').attr('transform', `translate(0,${plotH})`).call(d3.axisBottom(x))\n\n // 박스플롯 - 각 그룹별로 박스 생성\n groups.forEach(groupName => {\n // 해당 그룹의 데이터 찾기\n const groupData = this.data.find(d => this.getOrgValue(d) === groupName)\n console.log(`Searching for group: ${groupName}, found:`, groupData)\n if (!groupData) {\n console.warn(`No data found for group: ${groupName}. Available data:`, this.data.map(d => ({ org: d.org, keys: Object.keys(d) })))\n return\n }\n \n // 필수 필드 검증\n const requiredFields = [this.minKey, this.maxKey, this.q1Key, this.q3Key, this.medianKey, this.meanKey]\n const missingFields = requiredFields.filter(field => groupData[field] == null)\n if (missingFields.length > 0) {\n console.warn(`Missing required fields for group ${groupName}:`, missingFields)\n console.log('Group data:', groupData)\n return\n }\n \n const gx = x(groupName) ?? 0\n console.log(`Drawing box for ${groupName} at x=${gx}, bandwidth=${x.bandwidth()}`)\n\n // 독립 스케일 사용 시 해당 그룹의 스케일 찾기\n let currentY = y\n if (this.independentScale) {\n const values = [\n groupData[this.minKey],\n groupData[this.maxKey],\n groupData[this.q1Key],\n groupData[this.q3Key],\n groupData[this.medianKey],\n groupData[this.meanKey],\n groupData[this.valueKey]\n ]\n const min = d3.min(values) ?? 0\n const max = d3.max(values) ?? 1\n currentY = d3.scaleLinear().domain([min, max]).nice().range([plotH, 0])\n }\n\n // Outlier 계산 (1.5 * IQR 규칙)\n const iqr = groupData[this.q3Key] - groupData[this.q1Key]\n const lowerFence = groupData[this.q1Key] - 1.5 * iqr\n const upperFence = groupData[this.q3Key] + 1.5 * iqr\n\n // 실제 min/max 값 (fence 내부)\n const actualMin = Math.max(groupData[this.minKey], lowerFence)\n const actualMax = Math.min(groupData[this.maxKey], upperFence)\n\n // 박스\n g.append('rect')\n .attr('x', gx)\n .attr('y', currentY(groupData[this.q3Key]))\n .attr('width', x.bandwidth())\n .attr('height', currentY(groupData[this.q1Key]) - currentY(groupData[this.q3Key]))\n .attr('fill', this.getOrgValue(groupData) === this.currentGroup ? '#2196f3' : '#bbb')\n .attr('opacity', 0.5)\n\n // 중앙선(중앙값)\n g.append('line')\n .attr('x1', gx)\n .attr('x2', gx + x.bandwidth())\n .attr('y1', currentY(groupData[this.medianKey]))\n .attr('y2', currentY(groupData[this.medianKey]))\n .attr('stroke', '#333')\n .attr('stroke-width', 2)\n\n // 수염 (fence 내부의 min-max)\n g.append('line')\n .attr('x1', gx + x.bandwidth() / 2)\n .attr('x2', gx + x.bandwidth() / 2)\n .attr('y1', currentY(actualMin))\n .attr('y2', currentY(actualMax))\n .attr('stroke', '#333')\n\n // min/max 선 (fence 내부)\n g.append('line')\n .attr('x1', gx + x.bandwidth() / 4)\n .attr('x2', gx + (x.bandwidth() * 3) / 4)\n .attr('y1', currentY(actualMin))\n .attr('y2', currentY(actualMin))\n .attr('stroke', '#333')\n g.append('line')\n .attr('x1', gx + x.bandwidth() / 4)\n .attr('x2', gx + (x.bandwidth() * 3) / 4)\n .attr('y1', currentY(actualMax))\n .attr('y2', currentY(actualMax))\n .attr('stroke', '#333')\n\n // Outlier 표시 (fence 외부의 값들)\n if (groupData[this.minKey] < lowerFence) {\n g.append('circle')\n .attr('cx', gx + x.bandwidth() / 2)\n .attr('cy', currentY(groupData[this.minKey]))\n .attr('r', 3)\n .attr('fill', '#ff4444')\n .attr('stroke', '#333')\n .attr('stroke-width', 1)\n }\n if (groupData[this.maxKey] > upperFence) {\n g.append('circle')\n .attr('cx', gx + x.bandwidth() / 2)\n .attr('cy', currentY(groupData[this.maxKey]))\n .attr('r', 3)\n .attr('fill', '#ff4444')\n .attr('stroke', '#333')\n .attr('stroke-width', 1)\n }\n\n // 평균값\n g.append('circle')\n .attr('cx', gx + x.bandwidth() / 2)\n .attr('cy', currentY(groupData[this.meanKey]))\n .attr('r', 4)\n .attr('fill', 'orange')\n })\n // 현재 그룹 값 강조\n if (this.currentGroup && groups.includes(this.currentGroup)) {\n const currentGroupData = this.data.find(d => this.getOrgValue(d) === this.currentGroup)\n if (currentGroupData) {\n let currentY = y\n if (this.independentScale) {\n const values = [\n currentGroupData[this.minKey],\n currentGroupData[this.maxKey],\n currentGroupData[this.q1Key],\n currentGroupData[this.q3Key],\n currentGroupData[this.medianKey],\n currentGroupData[this.meanKey],\n currentGroupData[this.valueKey]\n ]\n const min = d3.min(values) ?? 0\n const max = d3.max(values) ?? 1\n currentY = d3.scaleLinear().domain([min, max]).nice().range([plotH, 0])\n }\n\n g.append('circle')\n .attr('cx', x(this.currentGroup) + x.bandwidth() / 2)\n .attr('cy', currentY(currentGroupData[this.valueKey]))\n .attr('r', 6)\n .attr('fill', '#e91e63')\n .attr('stroke', '#fff')\n .attr('stroke-width', 2)\n }\n }\n }\n}\n"]}
@@ -59,11 +59,11 @@ let KpiRadarChart = class KpiRadarChart extends LitElement {
59
59
  const h = this.chartHeight || 300;
60
60
  const r = Math.min(w, h) / 2 - 40;
61
61
  const angleSlice = (2 * Math.PI) / (this.categories.length || 1);
62
- // 데이터 변환: { group, values: [ {category, value} ... ] }
63
- const groupData = d3
64
- .groups(this.data, d => d.group)
65
- .map(([group, values]) => ({
66
- group,
62
+ // 데이터 변환: { org, values: [ {category, value} ... ] }
63
+ const orgData = d3
64
+ .groups(this.data, d => d.org)
65
+ .map(([org, values]) => ({
66
+ org,
67
67
  values: this.categories.map((cat, i) => {
68
68
  const found = values.find(v => v.category === cat);
69
69
  return { category: cat, value: found ? found[this.valueKey] : 0 };
@@ -99,7 +99,7 @@ let KpiRadarChart = class KpiRadarChart extends LitElement {
99
99
  .text(cat);
100
100
  });
101
101
  // 그룹별 폴리곤
102
- groupData.forEach(gd => {
102
+ orgData.forEach(gd => {
103
103
  // 마지막에 첫 점을 한 번 더 추가
104
104
  const closedValues = [...gd.values, gd.values[0]];
105
105
  const line = d3
@@ -109,9 +109,9 @@ let KpiRadarChart = class KpiRadarChart extends LitElement {
109
109
  g.append('path')
110
110
  .datum(closedValues)
111
111
  .attr('d', line)
112
- .attr('fill', gd.group === this.currentGroup ? 'rgba(33,150,243,0.4)' : 'rgba(200,200,200,0.2)')
113
- .attr('stroke', gd.group === this.currentGroup ? '#2196f3' : '#aaa')
114
- .attr('stroke-width', gd.group === this.currentGroup ? 3 : 1);
112
+ .attr('fill', gd.org === this.currentGroup ? 'rgba(33,150,243,0.4)' : 'rgba(200,200,200,0.2)')
113
+ .attr('stroke', gd.org === this.currentGroup ? '#2196f3' : '#aaa')
114
+ .attr('stroke-width', gd.org === this.currentGroup ? 3 : 1);
115
115
  });
116
116
  }
117
117
  };
@@ -1 +1 @@
1
- {"version":3,"file":"kpi-radar-chart.js","sourceRoot":"","sources":["../../client/charts/kpi-radar-chart.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, html, css } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport * as d3 from 'd3'\n\n@customElement('kpi-radar-chart')\nexport class KpiRadarChart extends LitElement {\n @property({ type: Array }) data: any[] = []\n @property({ type: Array }) categories: string[] = []\n @property({ type: String }) valueKey: string = 'value'\n @property({ type: String }) currentGroup: string = ''\n\n static styles = css`\n :host {\n display: block;\n width: 100%;\n height: 100%;\n }\n svg {\n width: 100%;\n height: 100%;\n display: block;\n }\n `\n\n private chartWidth = 0\n private chartHeight = 0\n private resizeObserver?: ResizeObserver\n\n render() {\n return html`<svg\n id=\"radar\"\n width=${this.chartWidth}\n height=${this.chartHeight}\n viewBox=\"0 0 ${this.chartWidth} ${this.chartHeight}\"\n preserveAspectRatio=\"xMidYMid meet\"\n ></svg>`\n }\n\n connectedCallback() {\n super.connectedCallback()\n this.resizeObserver = new ResizeObserver(entries => {\n for (const entry of entries) {\n const rect = entry.contentRect\n this.chartWidth = rect.width\n this.chartHeight = rect.height\n this.requestUpdate()\n }\n })\n this.resizeObserver.observe(this)\n }\n\n disconnectedCallback() {\n this.resizeObserver?.disconnect()\n super.disconnectedCallback()\n }\n\n updated() {\n this.drawRadar()\n }\n\n drawRadar() {\n const svg = d3.select(this.renderRoot.querySelector('#radar'))\n svg.selectAll('*').remove()\n const w = this.chartWidth || 300\n const h = this.chartHeight || 300\n const r = Math.min(w, h) / 2 - 40\n const angleSlice = (2 * Math.PI) / (this.categories.length || 1)\n\n // 데이터 변환: { group, values: [ {category, value} ... ] }\n const groupData = d3\n .groups(this.data, d => d.group)\n .map(([group, values]) => ({\n group,\n values: this.categories.map((cat, i) => {\n const found = values.find(v => v.category === cat)\n return { category: cat, value: found ? found[this.valueKey] : 0 }\n })\n }))\n\n // 스케일\n const maxValue = d3.max(this.data, d => d[this.valueKey]) || 1\n const radius = d3.scaleLinear().domain([0, maxValue]).range([0, r])\n\n // SVG 기본\n svg.attr('width', w).attr('height', h)\n const g = svg.append('g').attr('transform', `translate(${w / 2},${h / 2})`)\n\n // 그리드/축\n for (let i = 1; i <= 5; i++) {\n g.append('circle')\n .attr('r', (r / 5) * i)\n .attr('fill', 'none')\n .attr('stroke', '#ccc')\n }\n this.categories.forEach((cat, i) => {\n const angle = i * angleSlice - Math.PI / 2\n g.append('line')\n .attr('x1', 0)\n .attr('y1', 0)\n .attr('x2', radius(maxValue) * Math.cos(angle))\n .attr('y2', radius(maxValue) * Math.sin(angle))\n .attr('stroke', '#ccc')\n g.append('text')\n .attr('x', (radius(maxValue) + 10) * Math.cos(angle))\n .attr('y', (radius(maxValue) + 10) * Math.sin(angle))\n .attr('text-anchor', 'middle')\n .attr('alignment-baseline', 'middle')\n .attr('font-size', 12)\n .text(cat)\n })\n\n // 그룹별 폴리곤\n groupData.forEach(gd => {\n // 마지막에 첫 점을 한 번 더 추가\n const closedValues = [...gd.values, gd.values[0]]\n const line = d3\n .lineRadial()\n .radius((d: any) => radius(d.value))\n .angle((d, i) => i * angleSlice)\n g.append('path')\n .datum(closedValues)\n .attr('d', line as any)\n .attr('fill', gd.group === this.currentGroup ? 'rgba(33,150,243,0.4)' : 'rgba(200,200,200,0.2)')\n .attr('stroke', gd.group === this.currentGroup ? '#2196f3' : '#aaa')\n .attr('stroke-width', gd.group === this.currentGroup ? 3 : 1)\n })\n }\n}\n"]}
1
+ {"version":3,"file":"kpi-radar-chart.js","sourceRoot":"","sources":["../../client/charts/kpi-radar-chart.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, html, css } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport * as d3 from 'd3'\n\n@customElement('kpi-radar-chart')\nexport class KpiRadarChart extends LitElement {\n @property({ type: Array }) data: any[] = []\n @property({ type: Array }) categories: string[] = []\n @property({ type: String }) valueKey: string = 'value'\n @property({ type: String }) currentGroup: string = ''\n\n static styles = css`\n :host {\n display: block;\n width: 100%;\n height: 100%;\n }\n svg {\n width: 100%;\n height: 100%;\n display: block;\n }\n `\n\n private chartWidth = 0\n private chartHeight = 0\n private resizeObserver?: ResizeObserver\n\n render() {\n return html`<svg\n id=\"radar\"\n width=${this.chartWidth}\n height=${this.chartHeight}\n viewBox=\"0 0 ${this.chartWidth} ${this.chartHeight}\"\n preserveAspectRatio=\"xMidYMid meet\"\n ></svg>`\n }\n\n connectedCallback() {\n super.connectedCallback()\n this.resizeObserver = new ResizeObserver(entries => {\n for (const entry of entries) {\n const rect = entry.contentRect\n this.chartWidth = rect.width\n this.chartHeight = rect.height\n this.requestUpdate()\n }\n })\n this.resizeObserver.observe(this)\n }\n\n disconnectedCallback() {\n this.resizeObserver?.disconnect()\n super.disconnectedCallback()\n }\n\n updated() {\n this.drawRadar()\n }\n\n drawRadar() {\n const svg = d3.select(this.renderRoot.querySelector('#radar'))\n svg.selectAll('*').remove()\n const w = this.chartWidth || 300\n const h = this.chartHeight || 300\n const r = Math.min(w, h) / 2 - 40\n const angleSlice = (2 * Math.PI) / (this.categories.length || 1)\n\n // 데이터 변환: { org, values: [ {category, value} ... ] }\n const orgData = d3\n .groups(this.data, d => d.org)\n .map(([org, values]) => ({\n org,\n values: this.categories.map((cat, i) => {\n const found = values.find(v => v.category === cat)\n return { category: cat, value: found ? found[this.valueKey] : 0 }\n })\n }))\n\n // 스케일\n const maxValue = d3.max(this.data, d => d[this.valueKey]) || 1\n const radius = d3.scaleLinear().domain([0, maxValue]).range([0, r])\n\n // SVG 기본\n svg.attr('width', w).attr('height', h)\n const g = svg.append('g').attr('transform', `translate(${w / 2},${h / 2})`)\n\n // 그리드/축\n for (let i = 1; i <= 5; i++) {\n g.append('circle')\n .attr('r', (r / 5) * i)\n .attr('fill', 'none')\n .attr('stroke', '#ccc')\n }\n this.categories.forEach((cat, i) => {\n const angle = i * angleSlice - Math.PI / 2\n g.append('line')\n .attr('x1', 0)\n .attr('y1', 0)\n .attr('x2', radius(maxValue) * Math.cos(angle))\n .attr('y2', radius(maxValue) * Math.sin(angle))\n .attr('stroke', '#ccc')\n g.append('text')\n .attr('x', (radius(maxValue) + 10) * Math.cos(angle))\n .attr('y', (radius(maxValue) + 10) * Math.sin(angle))\n .attr('text-anchor', 'middle')\n .attr('alignment-baseline', 'middle')\n .attr('font-size', 12)\n .text(cat)\n })\n\n // 그룹별 폴리곤\n orgData.forEach(gd => {\n // 마지막에 첫 점을 한 번 더 추가\n const closedValues = [...gd.values, gd.values[0]]\n const line = d3\n .lineRadial()\n .radius((d: any) => radius(d.value))\n .angle((d, i) => i * angleSlice)\n g.append('path')\n .datum(closedValues)\n .attr('d', line as any)\n .attr('fill', gd.org === this.currentGroup ? 'rgba(33,150,243,0.4)' : 'rgba(200,200,200,0.2)')\n .attr('stroke', gd.org === this.currentGroup ? '#2196f3' : '#aaa')\n .attr('stroke-width', gd.org === this.currentGroup ? 3 : 1)\n })\n }\n}\n"]}
@@ -16,6 +16,11 @@ declare const KpiListPage_base: (new (...args: any[]) => {
16
16
  disconnectedCallback(): void;
17
17
  stateChanged(_state: unknown): void;
18
18
  readonly isConnected: boolean;
19
+ }) & (new (...args: any[]) => {
20
+ __preferenceProviders: {
21
+ [element: string]: import("@operato/p13n").PagePreferenceProvider;
22
+ };
23
+ getPagePreferenceProvider(element: string): import("@operato/p13n").PagePreferenceProvider | undefined;
19
24
  }) & (new (...args: any[]) => import("lit").LitElement) & typeof PageView & import("@open-wc/dedupe-mixin").Constructor<import("@open-wc/scoped-elements/types/src/types.js").ScopedElementsHost>;
20
25
  export declare class KpiListPage extends KpiListPage_base {
21
26
  static styles: import("lit").CSSResult[];
@@ -29,7 +34,8 @@ export declare class KpiListPage extends KpiListPage_base {
29
34
  private grist;
30
35
  availableVariables: any[];
31
36
  availableVariablesLoaded: boolean;
32
- getAvailableKpiMetricVariables(): Promise<any[]>;
37
+ hierarchicalView: boolean;
38
+ getAvailableKpiMetricVariables(currentKpi?: any): Promise<any>;
33
39
  get context(): {
34
40
  title: string;
35
41
  search: {
@@ -40,7 +46,11 @@ export declare class KpiListPage extends KpiListPage_base {
40
46
  handler: () => void;
41
47
  };
42
48
  help: string;
43
- actions: {
49
+ actions: ({
50
+ title: string;
51
+ action: () => void;
52
+ icon: string;
53
+ } | {
44
54
  icon: string;
45
55
  emphasis: {
46
56
  raised: boolean;
@@ -50,7 +60,7 @@ export declare class KpiListPage extends KpiListPage_base {
50
60
  };
51
61
  title: string;
52
62
  action: () => Promise<void>;
53
- }[];
63
+ })[];
54
64
  exportable: {
55
65
  name: string;
56
66
  data: () => Promise<{}[]>;
@@ -77,5 +87,11 @@ export declare class KpiListPage extends KpiListPage_base {
77
87
  _editViz(kpi: any): Promise<void>;
78
88
  _onVizUpdated(kpiId: string, vizType: string, vizMeta: any): Promise<void>;
79
89
  _calculateKpiValue(kpi: any): Promise<void>;
90
+ _toggleHierarchicalView(): void;
91
+ fetchHierarchicalData(): Promise<{
92
+ total: number;
93
+ records: any[];
94
+ }>;
95
+ flattenTreeData(treeData: any[], level?: number): any[];
80
96
  }
81
97
  export {};