@things-factory/kpi 9.0.31 → 9.0.32

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 (324) 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 +4 -3
  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.js +4 -3
  59. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js.map +1 -1
  60. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js +28 -30
  61. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js.map +1 -1
  62. package/dist-client/pages/kpi-history/kpi-history-list-page.d.ts +6 -1
  63. package/dist-client/pages/kpi-history/kpi-history-list-page.js +11 -11
  64. package/dist-client/pages/kpi-history/kpi-history-list-page.js.map +1 -1
  65. package/dist-client/pages/kpi-metric/kpi-metric-list-page.d.ts +5 -0
  66. package/dist-client/pages/kpi-metric/kpi-metric-list-page.js +10 -2
  67. package/dist-client/pages/kpi-metric/kpi-metric-list-page.js.map +1 -1
  68. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.d.ts +1 -1
  69. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js +8 -8
  70. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js.map +1 -1
  71. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js +2 -2
  72. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js.map +1 -1
  73. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.d.ts +5 -0
  74. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js +16 -8
  75. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js.map +1 -1
  76. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.d.ts +1 -1
  77. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js +6 -6
  78. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js.map +1 -1
  79. package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.js +1 -2
  80. package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.js.map +1 -1
  81. package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.d.ts +5 -0
  82. package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.js +10 -2
  83. package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.js.map +1 -1
  84. package/dist-client/pages/kpi-value/kpi-value-editor-page.d.ts +2 -1
  85. package/dist-client/pages/kpi-value/kpi-value-editor-page.js +16 -8
  86. package/dist-client/pages/kpi-value/kpi-value-editor-page.js.map +1 -1
  87. package/dist-client/pages/kpi-value/kpi-value-list-page.d.ts +5 -0
  88. package/dist-client/pages/kpi-value/kpi-value-list-page.js +31 -7
  89. package/dist-client/pages/kpi-value/kpi-value-list-page.js.map +1 -1
  90. package/dist-client/route.d.ts +1 -1
  91. package/dist-client/route.js +2 -8
  92. package/dist-client/route.js.map +1 -1
  93. package/dist-client/tsconfig.tsbuildinfo +1 -1
  94. package/dist-server/controllers/kpi-metric-value-provider.d.ts +1 -1
  95. package/dist-server/controllers/kpi-metric-value-provider.js +4 -4
  96. package/dist-server/controllers/kpi-metric-value-provider.js.map +1 -1
  97. package/dist-server/controllers/kpi-value-provider.d.ts +1 -1
  98. package/dist-server/controllers/kpi-value-provider.js +3 -3
  99. package/dist-server/controllers/kpi-value-provider.js.map +1 -1
  100. package/dist-server/migrations/1752190849680-seed-kpi-metrics.d.ts +6 -0
  101. package/dist-server/migrations/1752190849680-seed-kpi-metrics.js +101 -0
  102. package/dist-server/migrations/1752190849680-seed-kpi-metrics.js.map +1 -0
  103. package/dist-server/migrations/1752190849681-seed-kpi.d.ts +5 -0
  104. package/dist-server/migrations/1752190849681-seed-kpi.js +315 -0
  105. package/dist-server/migrations/1752190849681-seed-kpi.js.map +1 -0
  106. package/dist-server/migrations/1752192090123-add-grades-to-kpi.d.ts +7 -0
  107. package/dist-server/migrations/1752192090123-add-grades-to-kpi.js +51 -0
  108. package/dist-server/migrations/1752192090123-add-grades-to-kpi.js.map +1 -0
  109. package/dist-server/migrations/1752192090124-add-kpi-statistics.d.ts +5 -0
  110. package/dist-server/migrations/1752192090124-add-kpi-statistics.js +710 -0
  111. package/dist-server/migrations/1752192090124-add-kpi-statistics.js.map +1 -0
  112. package/dist-server/migrations/1752192090128-seed-kpi-org-scope.d.ts +6 -0
  113. package/dist-server/migrations/1752192090128-seed-kpi-org-scope.js +111 -0
  114. package/dist-server/migrations/1752192090128-seed-kpi-org-scope.js.map +1 -0
  115. package/dist-server/migrations/1752192090129-seed-kpi-values.d.ts +6 -0
  116. package/dist-server/migrations/1752192090129-seed-kpi-values.js +187 -0
  117. package/dist-server/migrations/1752192090129-seed-kpi-values.js.map +1 -0
  118. package/dist-server/migrations/grade-data/x11-performance-table.json +962 -0
  119. package/dist-server/migrations/grade-data/x12-performance-table.json +611 -0
  120. package/dist-server/migrations/grade-data/x14-performance-table.json +42 -0
  121. package/dist-server/migrations/grade-data/x21-performance-table.json +889 -0
  122. package/dist-server/migrations/grade-data/x22-performance-table.json +1064 -0
  123. package/dist-server/migrations/grade-data/x23-performance-table.json +42 -0
  124. package/dist-server/migrations/grade-data/x31-performance-table.json +644 -0
  125. package/dist-server/migrations/grade-data/x32-performance-table.json +993 -0
  126. package/dist-server/migrations/grade-data/x33-performance-table.json +195 -0
  127. package/dist-server/migrations/grade-data/x34-performance-table.json +12 -0
  128. package/dist-server/migrations/grade-data/x35-performance-table.json +42 -0
  129. package/dist-server/migrations/grade-data/x41-performance-table.json +825 -0
  130. package/dist-server/migrations/grade-data/x42-performance-table.json +786 -0
  131. package/dist-server/migrations/grade-data/x43-performance-table.json +12 -0
  132. package/dist-server/migrations/grade-data/x44-performance-table.json +42 -0
  133. package/dist-server/migrations/grade-data/x51-performance-table.json +924 -0
  134. package/dist-server/migrations/grade-data/x52-performance-table.json +42 -0
  135. package/dist-server/migrations/grade-data/x61-performance-table.json +261 -0
  136. package/dist-server/migrations/grade-data/x62-performance-table.json +42 -0
  137. package/dist-server/migrations/seed-data/kpi-metrics-seed.json +454 -0
  138. package/dist-server/migrations/seed-data/kpi-org-scope-seed.json +1676 -0
  139. package/dist-server/migrations/seed-data/kpi-values-seed.json +402 -0
  140. package/dist-server/migrations/seed-data/kpis-seed.json +488 -0
  141. package/dist-server/service/index.d.ts +3 -7
  142. package/dist-server/service/index.js +5 -12
  143. package/dist-server/service/index.js.map +1 -1
  144. package/dist-server/service/kpi/aggregate-kpi.js +30 -13
  145. package/dist-server/service/kpi/aggregate-kpi.js.map +1 -1
  146. package/dist-server/service/kpi/kpi-formula.service.d.ts +15 -0
  147. package/dist-server/service/kpi/kpi-formula.service.js +90 -0
  148. package/dist-server/service/kpi/kpi-formula.service.js.map +1 -1
  149. package/dist-server/service/kpi/kpi-history.d.ts +0 -3
  150. package/dist-server/service/kpi/kpi-history.js +0 -10
  151. package/dist-server/service/kpi/kpi-history.js.map +1 -1
  152. package/dist-server/service/kpi/kpi-mutation.d.ts +1 -1
  153. package/dist-server/service/kpi/kpi-mutation.js +57 -20
  154. package/dist-server/service/kpi/kpi-mutation.js.map +1 -1
  155. package/dist-server/service/kpi/kpi-query.d.ts +7 -3
  156. package/dist-server/service/kpi/kpi-query.js +126 -10
  157. package/dist-server/service/kpi/kpi-query.js.map +1 -1
  158. package/dist-server/service/kpi/kpi-type.d.ts +4 -2
  159. package/dist-server/service/kpi/kpi-type.js +12 -4
  160. package/dist-server/service/kpi/kpi-type.js.map +1 -1
  161. package/dist-server/service/kpi/kpi.d.ts +4 -3
  162. package/dist-server/service/kpi/kpi.js +20 -8
  163. package/dist-server/service/kpi/kpi.js.map +1 -1
  164. package/dist-server/service/kpi-metric/aggregate-kpi-metric.js +46 -11
  165. package/dist-server/service/kpi-metric/aggregate-kpi-metric.js.map +1 -1
  166. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.d.ts +1 -1
  167. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js +6 -6
  168. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js.map +1 -1
  169. package/dist-server/service/kpi-metric-value/kpi-metric-value-type.d.ts +2 -2
  170. package/dist-server/service/kpi-metric-value/kpi-metric-value-type.js +4 -4
  171. package/dist-server/service/kpi-metric-value/kpi-metric-value-type.js.map +1 -1
  172. package/dist-server/service/kpi-metric-value/kpi-metric-value.d.ts +1 -1
  173. package/dist-server/service/kpi-metric-value/kpi-metric-value.js +3 -3
  174. package/dist-server/service/kpi-metric-value/kpi-metric-value.js.map +1 -1
  175. package/dist-server/service/kpi-org-scope/index.d.ts +9 -0
  176. package/dist-server/service/kpi-org-scope/index.js +14 -0
  177. package/dist-server/service/kpi-org-scope/index.js.map +1 -0
  178. package/dist-server/service/kpi-org-scope/kpi-org-scope-mutation.d.ts +8 -0
  179. package/dist-server/service/kpi-org-scope/kpi-org-scope-mutation.js +170 -0
  180. package/dist-server/service/kpi-org-scope/kpi-org-scope-mutation.js.map +1 -0
  181. package/dist-server/service/kpi-org-scope/kpi-org-scope-query.d.ts +14 -0
  182. package/dist-server/service/kpi-org-scope/kpi-org-scope-query.js +152 -0
  183. package/dist-server/service/kpi-org-scope/kpi-org-scope-query.js.map +1 -0
  184. package/dist-server/service/kpi-org-scope/kpi-org-scope-type.d.ts +26 -0
  185. package/dist-server/service/kpi-org-scope/kpi-org-scope-type.js +101 -0
  186. package/dist-server/service/kpi-org-scope/kpi-org-scope-type.js.map +1 -0
  187. package/dist-server/service/kpi-org-scope/kpi-org-scope.d.ts +26 -0
  188. package/dist-server/service/kpi-org-scope/kpi-org-scope.js +135 -0
  189. package/dist-server/service/kpi-org-scope/kpi-org-scope.js.map +1 -0
  190. package/dist-server/service/kpi-statistic/kpi-statistic.d.ts +1 -0
  191. package/dist-server/service/kpi-statistic/kpi-statistic.js +11 -0
  192. package/dist-server/service/kpi-statistic/kpi-statistic.js.map +1 -1
  193. package/dist-server/service/kpi-value/kpi-value-mutation.js +71 -7
  194. package/dist-server/service/kpi-value/kpi-value-mutation.js.map +1 -1
  195. package/dist-server/service/kpi-value/kpi-value-type.d.ts +4 -2
  196. package/dist-server/service/kpi-value/kpi-value-type.js +12 -4
  197. package/dist-server/service/kpi-value/kpi-value-type.js.map +1 -1
  198. package/dist-server/service/kpi-value/kpi-value.d.ts +3 -1
  199. package/dist-server/service/kpi-value/kpi-value.js +11 -5
  200. package/dist-server/service/kpi-value/kpi-value.js.map +1 -1
  201. package/dist-server/service/utils/value-date-util.d.ts +1 -0
  202. package/dist-server/service/utils/value-date-util.js +41 -0
  203. package/dist-server/service/utils/value-date-util.js.map +1 -1
  204. package/dist-server/tsconfig.json +10 -0
  205. package/dist-server/tsconfig.tsbuildinfo +1 -1
  206. package/package.json +7 -6
  207. package/server/@types/index.d.ts +11 -0
  208. package/server/controllers/kpi-metric-value-provider.ts +5 -5
  209. package/server/controllers/kpi-value-provider.ts +4 -4
  210. package/server/migrations/1752190849680-seed-kpi-metrics.ts +124 -0
  211. package/server/migrations/1752190849681-seed-kpi.ts +356 -0
  212. package/server/migrations/1752192090123-add-grades-to-kpi.ts +67 -0
  213. package/server/migrations/1752192090124-add-kpi-statistics.ts +719 -0
  214. package/server/migrations/1752192090128-seed-kpi-org-scope.ts +132 -0
  215. package/server/migrations/1752192090129-seed-kpi-values.ts +207 -0
  216. package/server/migrations/grade-data/x11-performance-table.json +962 -0
  217. package/server/migrations/grade-data/x12-performance-table.json +611 -0
  218. package/server/migrations/grade-data/x14-performance-table.json +42 -0
  219. package/server/migrations/grade-data/x21-performance-table.json +889 -0
  220. package/server/migrations/grade-data/x22-performance-table.json +1064 -0
  221. package/server/migrations/grade-data/x23-performance-table.json +42 -0
  222. package/server/migrations/grade-data/x31-performance-table.json +644 -0
  223. package/server/migrations/grade-data/x32-performance-table.json +993 -0
  224. package/server/migrations/grade-data/x33-performance-table.json +195 -0
  225. package/server/migrations/grade-data/x34-performance-table.json +12 -0
  226. package/server/migrations/grade-data/x35-performance-table.json +42 -0
  227. package/server/migrations/grade-data/x41-performance-table.json +825 -0
  228. package/server/migrations/grade-data/x42-performance-table.json +786 -0
  229. package/server/migrations/grade-data/x43-performance-table.json +12 -0
  230. package/server/migrations/grade-data/x44-performance-table.json +42 -0
  231. package/server/migrations/grade-data/x51-performance-table.json +924 -0
  232. package/server/migrations/grade-data/x52-performance-table.json +42 -0
  233. package/server/migrations/grade-data/x61-performance-table.json +261 -0
  234. package/server/migrations/grade-data/x62-performance-table.json +42 -0
  235. package/server/migrations/seed-data/kpi-metrics-seed.json +454 -0
  236. package/server/migrations/seed-data/kpi-org-scope-seed.json +1676 -0
  237. package/server/migrations/seed-data/kpi-values-seed.json +402 -0
  238. package/server/migrations/seed-data/kpis-seed.json +488 -0
  239. package/server/service/index.ts +5 -12
  240. package/server/service/kpi/aggregate-kpi.ts +31 -13
  241. package/server/service/kpi/kpi-formula.service.ts +101 -0
  242. package/server/service/kpi/kpi-history.ts +0 -8
  243. package/server/service/kpi/kpi-mutation.ts +59 -19
  244. package/server/service/kpi/kpi-query.ts +118 -8
  245. package/server/service/kpi/kpi-type.ts +10 -4
  246. package/server/service/kpi/kpi.ts +17 -7
  247. package/server/service/kpi-metric/aggregate-kpi-metric.ts +55 -11
  248. package/server/service/kpi-metric-value/kpi-metric-value-mutation.ts +6 -6
  249. package/server/service/kpi-metric-value/kpi-metric-value-type.ts +4 -4
  250. package/server/service/kpi-metric-value/kpi-metric-value.ts +3 -3
  251. package/server/service/kpi-org-scope/index.ts +11 -0
  252. package/server/service/kpi-org-scope/kpi-org-scope-mutation.ts +173 -0
  253. package/server/service/kpi-org-scope/kpi-org-scope-query.ts +127 -0
  254. package/server/service/kpi-org-scope/kpi-org-scope-type.ts +68 -0
  255. package/server/service/kpi-org-scope/kpi-org-scope.ts +123 -0
  256. package/server/service/kpi-statistic/kpi-statistic.ts +10 -0
  257. package/server/service/kpi-value/kpi-value-mutation.ts +73 -7
  258. package/server/service/kpi-value/kpi-value-type.ts +10 -4
  259. package/server/service/kpi-value/kpi-value.ts +10 -5
  260. package/server/service/utils/value-date-util.ts +47 -0
  261. package/server/types/global.d.ts +8 -0
  262. package/things-factory.config.js +1 -0
  263. package/translations/en.json +15 -3
  264. package/translations/ja.json +13 -3
  265. package/translations/ko.json +15 -3
  266. package/translations/ms.json +13 -3
  267. package/translations/zh.json +13 -3
  268. package/client/pages/kpi-category/kpi-category-importer.ts +0 -90
  269. package/client/pages/kpi-category/kpi-category-list-page.ts +0 -537
  270. package/client/pages/kpi-category/kpi-category-value-calculator.ts +0 -233
  271. package/client/pages/kpi-category-value/kpi-category-value-list-page.ts +0 -404
  272. package/dist-client/pages/kpi-category/kpi-category-importer.d.ts +0 -23
  273. package/dist-client/pages/kpi-category/kpi-category-importer.js +0 -92
  274. package/dist-client/pages/kpi-category/kpi-category-importer.js.map +0 -1
  275. package/dist-client/pages/kpi-category/kpi-category-list-page.d.ts +0 -74
  276. package/dist-client/pages/kpi-category/kpi-category-list-page.js +0 -517
  277. package/dist-client/pages/kpi-category/kpi-category-list-page.js.map +0 -1
  278. package/dist-client/pages/kpi-category/kpi-category-value-calculator.d.ts +0 -13
  279. package/dist-client/pages/kpi-category/kpi-category-value-calculator.js +0 -256
  280. package/dist-client/pages/kpi-category/kpi-category-value-calculator.js.map +0 -1
  281. package/dist-client/pages/kpi-category-value/kpi-category-value-list-page.d.ts +0 -63
  282. package/dist-client/pages/kpi-category-value/kpi-category-value-list-page.js +0 -393
  283. package/dist-client/pages/kpi-category-value/kpi-category-value-list-page.js.map +0 -1
  284. package/dist-server/service/kpi-category/index.d.ts +0 -6
  285. package/dist-server/service/kpi-category/index.js +0 -10
  286. package/dist-server/service/kpi-category/index.js.map +0 -1
  287. package/dist-server/service/kpi-category/kpi-category-mutation.d.ts +0 -9
  288. package/dist-server/service/kpi-category/kpi-category-mutation.js +0 -221
  289. package/dist-server/service/kpi-category/kpi-category-mutation.js.map +0 -1
  290. package/dist-server/service/kpi-category/kpi-category-query.d.ts +0 -18
  291. package/dist-server/service/kpi-category/kpi-category-query.js +0 -115
  292. package/dist-server/service/kpi-category/kpi-category-query.js.map +0 -1
  293. package/dist-server/service/kpi-category/kpi-category-type.d.ts +0 -24
  294. package/dist-server/service/kpi-category/kpi-category-type.js +0 -100
  295. package/dist-server/service/kpi-category/kpi-category-type.js.map +0 -1
  296. package/dist-server/service/kpi-category/kpi-category.d.ts +0 -22
  297. package/dist-server/service/kpi-category/kpi-category.js +0 -106
  298. package/dist-server/service/kpi-category/kpi-category.js.map +0 -1
  299. package/dist-server/service/kpi-category-value/index.d.ts +0 -6
  300. package/dist-server/service/kpi-category-value/index.js +0 -10
  301. package/dist-server/service/kpi-category-value/index.js.map +0 -1
  302. package/dist-server/service/kpi-category-value/kpi-category-value-mutation.d.ts +0 -8
  303. package/dist-server/service/kpi-category-value/kpi-category-value-mutation.js +0 -102
  304. package/dist-server/service/kpi-category-value/kpi-category-value-mutation.js.map +0 -1
  305. package/dist-server/service/kpi-category-value/kpi-category-value-query.d.ts +0 -13
  306. package/dist-server/service/kpi-category-value/kpi-category-value-query.js +0 -91
  307. package/dist-server/service/kpi-category-value/kpi-category-value-query.js.map +0 -1
  308. package/dist-server/service/kpi-category-value/kpi-category-value-type.d.ts +0 -19
  309. package/dist-server/service/kpi-category-value/kpi-category-value-type.js +0 -73
  310. package/dist-server/service/kpi-category-value/kpi-category-value-type.js.map +0 -1
  311. package/dist-server/service/kpi-category-value/kpi-category-value.d.ts +0 -19
  312. package/dist-server/service/kpi-category-value/kpi-category-value.js +0 -91
  313. package/dist-server/service/kpi-category-value/kpi-category-value.js.map +0 -1
  314. package/helps/kpi/kpi-category.md +0 -160
  315. package/server/service/kpi-category/index.ts +0 -7
  316. package/server/service/kpi-category/kpi-category-mutation.ts +0 -217
  317. package/server/service/kpi-category/kpi-category-query.ts +0 -87
  318. package/server/service/kpi-category/kpi-category-type.ts +0 -73
  319. package/server/service/kpi-category/kpi-category.ts +0 -95
  320. package/server/service/kpi-category-value/index.ts +0 -7
  321. package/server/service/kpi-category-value/kpi-category-value-mutation.ts +0 -88
  322. package/server/service/kpi-category-value/kpi-category-value-query.ts +0 -62
  323. package/server/service/kpi-category-value/kpi-category-value-type.ts +0 -48
  324. package/server/service/kpi-category-value/kpi-category-value.ts +0 -79
@@ -1,12 +1,11 @@
1
-
2
1
  # KPI 모듈 엔티티 설계
3
2
 
4
3
  ## 1. KPI (핵심성과지표)
4
+
5
5
  - **id**: string (PK)
6
6
  - **name**: string (지표명)
7
7
  - **code**: string (지표코드, unique)
8
8
  - **description**: string (설명)
9
- - **categoryId**: string (FK, KPI 카테고리)
10
9
  - **unit**: string (단위)
11
10
  - **formula**: string (산식)
12
11
  - **targetValue**: number (목표값)
@@ -16,13 +15,8 @@
16
15
  - **createdAt**: datetime
17
16
  - **updatedAt**: datetime
18
17
 
19
- ## 2. KPI_CATEGORY (지표 분류)
20
- - **id**: string (PK)
21
- - **name**: string (분류명)
22
- - **description**: string
23
- - **parentId**: string (FK, 상위 분류)
24
-
25
18
  ## 3. KPI_VALUE (지표 실적값)
19
+
26
20
  - **id**: string (PK)
27
21
  - **kpiId**: string (FK, KPI)
28
22
  - **date**: date (실적 기준일)
@@ -32,13 +26,14 @@
32
26
  - **createdAt**: datetime
33
27
 
34
28
  ## 4. KPI_OWNER (담당자/조직)
29
+
35
30
  - **id**: string (PK)
36
31
  - **name**: string (이름/조직명)
37
32
  - **type**: enum('USER', 'DEPT')
38
33
  - **email**: string
39
34
 
40
-
41
35
  ## 5. KPI_HISTORY (변경이력)
36
+
42
37
  - **id**: string (PK)
43
38
  - **kpiId**: string (FK, KPI)
44
39
  - **changedAt**: datetime
@@ -48,6 +43,7 @@
48
43
  - **newValue**: string
49
44
 
50
45
  ## 6. METRIC (측정지표)
46
+
51
47
  - **id**: string (PK)
52
48
  - **name**: string (지표명)
53
49
  - **code**: string (지표코드, unique)
@@ -57,14 +53,15 @@
57
53
  - **createdAt**: datetime
58
54
  - **updatedAt**: datetime
59
55
 
60
-
61
56
  ## 7. KPI_METRIC_MAP (KPI-메트릭 매핑)
57
+
62
58
  - **id**: string (PK)
63
59
  - **kpiId**: string (FK, KPI)
64
60
  - **metricId**: string (FK, METRIC)
65
61
  - **order**: number (산식 내 사용 순서/우선순위)
66
62
 
67
63
  ## 8. KPI_GRADE (KPI별 등급/구간)
64
+
68
65
  - **id**: string (PK)
69
66
  - **kpiId**: string (FK, KPI)
70
67
  - **grade**: string (등급명, 예: A, B, C, D)
@@ -79,10 +76,8 @@
79
76
  ## 엔티티 간 릴레이션
80
77
 
81
78
  - **KPI** 1 --- N **KPI_VALUE** (한 KPI에 여러 실적값)
82
- - **KPI** N --- 1 **KPI_CATEGORY** (여러 KPI가 한 분류에 속함)
83
79
  - **KPI** N --- 1 **KPI_OWNER** (여러 KPI가 한 담당자/조직에 속함)
84
80
  - **KPI** 1 --- N **KPI_HISTORY** (한 KPI에 여러 변경이력)
85
- - **KPI_CATEGORY** N --- 1 **KPI_CATEGORY** (상위-하위 분류 구조)
86
81
  - **KPI** N --- M **METRIC** (KPI와 Metric은 N:M, KPI_METRIC_MAP으로 연결)
87
82
 
88
83
  - **KPI** 1 --- N **KPI_GRADE** (한 KPI에 여러 등급/구간)
@@ -90,6 +85,7 @@
90
85
  ---
91
86
 
92
87
  ## 확장 고려사항
88
+
93
89
  - KPI별 첨부파일, 알림설정, 외부연계정보 등은 추후 별도 엔티티로 확장 가능
94
90
 
95
91
  ---
@@ -10,6 +10,7 @@ export declare class KpiBoxplotChart extends LitElement {
10
10
  q3Key: string;
11
11
  valueKey: string;
12
12
  currentGroup: string;
13
+ independentScale: boolean;
13
14
  static styles: import("lit").CSSResult;
14
15
  private chartWidth;
15
16
  private chartHeight;
@@ -18,5 +19,6 @@ export declare class KpiBoxplotChart extends LitElement {
18
19
  connectedCallback(): void;
19
20
  disconnectedCallback(): void;
20
21
  updated(): void;
22
+ private getOrgValue;
21
23
  drawBoxplot(): void;
22
24
  }
@@ -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 {};