@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
package/README.md CHANGED
@@ -3,14 +3,13 @@
3
3
  ## 1. KPI란?
4
4
 
5
5
  - KPI(Key Performance Indicator)는 조직의 목표 달성도를 수치로 측정/관리하는 핵심 지표입니다.
6
- - KPI는 산식(formula), 목표값(target), 카테고리(category), 버전(version), 상태(state) 등 다양한 속성을 가집니다.
6
+ - KPI는 산식(formula), 목표값(target), 버전(version), 상태(state) 등 다양한 속성을 가집니다.
7
7
  - KPI의 실적값(Performance Value)은 특정 시점/조직/프로젝트별로 기록됩니다.
8
8
 
9
9
  ## 2. KPI 모듈의 주요 엔티티
10
10
 
11
11
  - **Kpi**: KPI 정의(이름, 산식, 카테고리, 상태, 버전 등)
12
12
  - **KpiValue**: KPI별 실적값(날짜, 값, 입력방식, 소스 등)
13
- - **KpiCategory**: KPI 분류(트리 구조 가능)
14
13
  - **KpiMetric**: KPI 산식에 사용되는 원천 데이터 항목(코드, 단위, 데이터셋 등)
15
14
  - **KpiGrade**: KPI별 등급/구간(구간별 점수, 색상 등)
16
15
  - **KpiHistory**: KPI의 버전별 이력(스냅샷)
@@ -5,7 +5,7 @@ import * as d3 from 'd3'
5
5
  @customElement('kpi-boxplot-chart')
6
6
  export class KpiBoxplotChart extends LitElement {
7
7
  @property({ type: Array }) data: any[] = []
8
- @property({ type: Array }) groups: string[] = []
8
+ @property({ type: Array }) groups: string[] = [] // 조직 단위 목록 (빈 배열이면 데이터에서 자동 추출)
9
9
  @property({ type: String }) minKey: string = 'min'
10
10
  @property({ type: String }) maxKey: string = 'max'
11
11
  @property({ type: String }) meanKey: string = 'mean'
@@ -14,6 +14,7 @@ export class KpiBoxplotChart extends LitElement {
14
14
  @property({ type: String }) q3Key: string = 'q3'
15
15
  @property({ type: String }) valueKey: string = 'value'
16
16
  @property({ type: String }) currentGroup: string = ''
17
+ @property({ type: Boolean }) independentScale: boolean = false
17
18
 
18
19
  static styles = css`
19
20
  :host {
@@ -64,32 +65,83 @@ export class KpiBoxplotChart extends LitElement {
64
65
  this.drawBoxplot()
65
66
  }
66
67
 
68
+ private getOrgValue = (d: any) => d.org || d.group
69
+
67
70
  drawBoxplot() {
68
71
  const svg = d3.select(this.renderRoot.querySelector('#boxplot'))
69
72
  svg.selectAll('*').remove()
73
+
74
+ // 데이터 검증
75
+ if (!this.data || this.data.length === 0) {
76
+ return
77
+ }
70
78
  const w = this.chartWidth || 300
71
79
  const h = this.chartHeight || 300
72
- const margin = { top: 20, right: 20, bottom: 40, left: 40 }
80
+ const margin = {
81
+ top: 20,
82
+ right: 20,
83
+ bottom: 40,
84
+ left: this.independentScale ? 20 : 40
85
+ }
73
86
  const plotW = w - margin.left - margin.right
74
87
  const plotH = h - margin.top - margin.bottom
75
88
 
76
- // x축: 그룹
77
- const x = d3.scaleBand().domain(this.groups).range([0, plotW]).padding(0.4)
78
- // y축:
79
- const allValues = this.data.flatMap(d => [
80
- d[this.minKey],
81
- d[this.maxKey],
82
- d[this.q1Key],
83
- d[this.q3Key],
84
- d[this.medianKey],
85
- d[this.meanKey],
86
- d[this.valueKey]
87
- ])
88
- const y = d3
89
- .scaleLinear()
90
- .domain([d3.min(allValues) ?? 0, d3.max(allValues) ?? 1])
91
- .nice()
92
- .range([plotH, 0])
89
+ // x축: 조직 단위 (그룹) - org 또는 group 필드 지원
90
+ const groups = this.groups.length > 0 ? this.groups : [...new Set(this.data.map(d => this.getOrgValue(d)).filter(org => org != null))]
91
+ if (groups.length === 0) {
92
+ console.warn('No valid groups found in data:', this.data)
93
+ return // 유효한 그룹이 없으면 차트를 그리지 않음
94
+ }
95
+
96
+ console.log('Boxplot groups:', groups)
97
+ console.log('Data org values:', this.data.map(d => this.getOrgValue(d)))
98
+ console.log('Full data:', this.data)
99
+ console.log('Chart dimensions:', { w, h, plotW, plotH })
100
+
101
+ const x = d3.scaleBand().domain(groups).range([0, plotW]).padding(0.4)
102
+
103
+ // y축 스케일 설정
104
+ let y: d3.ScaleLinear<number, number>
105
+
106
+ if (this.independentScale) {
107
+ // 독립 스케일: 각 시리즈별로 개별 스케일 생성
108
+ const yScales = this.data.map(d => {
109
+ const values = [
110
+ d[this.minKey],
111
+ d[this.maxKey],
112
+ d[this.q1Key],
113
+ d[this.q3Key],
114
+ d[this.medianKey],
115
+ d[this.meanKey],
116
+ d[this.valueKey]
117
+ ]
118
+ const min = d3.min(values) ?? 0
119
+ const max = d3.max(values) ?? 1
120
+ return {
121
+ org: d.org,
122
+ scale: d3.scaleLinear().domain([min, max]).nice().range([plotH, 0])
123
+ }
124
+ })
125
+
126
+ // 기본 y축은 첫 번째 스케일 사용
127
+ y = yScales[0]?.scale || d3.scaleLinear().domain([0, 1]).range([plotH, 0])
128
+ } else {
129
+ // 통합 스케일: 모든 데이터를 하나의 스케일에 맞춤
130
+ const allValues = this.data.flatMap(d => [
131
+ d[this.minKey],
132
+ d[this.maxKey],
133
+ d[this.q1Key],
134
+ d[this.q3Key],
135
+ d[this.medianKey],
136
+ d[this.meanKey],
137
+ d[this.valueKey]
138
+ ])
139
+ y = d3
140
+ .scaleLinear()
141
+ .domain([d3.min(allValues) ?? 0, d3.max(allValues) ?? 1])
142
+ .nice()
143
+ .range([plotH, 0])
144
+ }
93
145
 
94
146
  const g = svg
95
147
  .attr('width', w)
@@ -98,66 +150,154 @@ export class KpiBoxplotChart extends LitElement {
98
150
  .attr('transform', `translate(${margin.left},${margin.top})`)
99
151
 
100
152
  // 축
101
- g.append('g').call(d3.axisLeft(y))
153
+ if (!this.independentScale) {
154
+ g.append('g').call(d3.axisLeft(y))
155
+ }
102
156
  g.append('g').attr('transform', `translate(0,${plotH})`).call(d3.axisBottom(x))
103
157
 
104
- // 박스플롯
105
- this.data.forEach(d => {
106
- const gx = x(d.group) ?? 0
158
+ // 박스플롯 - 각 그룹별로 박스 생성
159
+ groups.forEach(groupName => {
160
+ // 해당 그룹의 데이터 찾기
161
+ const groupData = this.data.find(d => this.getOrgValue(d) === groupName)
162
+ console.log(`Searching for group: ${groupName}, found:`, groupData)
163
+ if (!groupData) {
164
+ console.warn(`No data found for group: ${groupName}. Available data:`, this.data.map(d => ({ org: d.org, keys: Object.keys(d) })))
165
+ return
166
+ }
167
+
168
+ // 필수 필드 검증
169
+ const requiredFields = [this.minKey, this.maxKey, this.q1Key, this.q3Key, this.medianKey, this.meanKey]
170
+ const missingFields = requiredFields.filter(field => groupData[field] == null)
171
+ if (missingFields.length > 0) {
172
+ console.warn(`Missing required fields for group ${groupName}:`, missingFields)
173
+ console.log('Group data:', groupData)
174
+ return
175
+ }
176
+
177
+ const gx = x(groupName) ?? 0
178
+ console.log(`Drawing box for ${groupName} at x=${gx}, bandwidth=${x.bandwidth()}`)
179
+
180
+ // 독립 스케일 사용 시 해당 그룹의 스케일 찾기
181
+ let currentY = y
182
+ if (this.independentScale) {
183
+ const values = [
184
+ groupData[this.minKey],
185
+ groupData[this.maxKey],
186
+ groupData[this.q1Key],
187
+ groupData[this.q3Key],
188
+ groupData[this.medianKey],
189
+ groupData[this.meanKey],
190
+ groupData[this.valueKey]
191
+ ]
192
+ const min = d3.min(values) ?? 0
193
+ const max = d3.max(values) ?? 1
194
+ currentY = d3.scaleLinear().domain([min, max]).nice().range([plotH, 0])
195
+ }
196
+
197
+ // Outlier 계산 (1.5 * IQR 규칙)
198
+ const iqr = groupData[this.q3Key] - groupData[this.q1Key]
199
+ const lowerFence = groupData[this.q1Key] - 1.5 * iqr
200
+ const upperFence = groupData[this.q3Key] + 1.5 * iqr
201
+
202
+ // 실제 min/max 값 (fence 내부)
203
+ const actualMin = Math.max(groupData[this.minKey], lowerFence)
204
+ const actualMax = Math.min(groupData[this.maxKey], upperFence)
205
+
107
206
  // 박스
108
207
  g.append('rect')
109
208
  .attr('x', gx)
110
- .attr('y', y(d[this.q3Key]))
209
+ .attr('y', currentY(groupData[this.q3Key]))
111
210
  .attr('width', x.bandwidth())
112
- .attr('height', y(d[this.q1Key]) - y(d[this.q3Key]))
113
- .attr('fill', d.group === this.currentGroup ? '#2196f3' : '#bbb')
211
+ .attr('height', currentY(groupData[this.q1Key]) - currentY(groupData[this.q3Key]))
212
+ .attr('fill', this.getOrgValue(groupData) === this.currentGroup ? '#2196f3' : '#bbb')
114
213
  .attr('opacity', 0.5)
214
+
115
215
  // 중앙선(중앙값)
116
216
  g.append('line')
117
217
  .attr('x1', gx)
118
218
  .attr('x2', gx + x.bandwidth())
119
- .attr('y1', y(d[this.medianKey]))
120
- .attr('y2', y(d[this.medianKey]))
219
+ .attr('y1', currentY(groupData[this.medianKey]))
220
+ .attr('y2', currentY(groupData[this.medianKey]))
121
221
  .attr('stroke', '#333')
122
222
  .attr('stroke-width', 2)
123
- // 수염(min-max)
223
+
224
+ // 수염 (fence 내부의 min-max)
124
225
  g.append('line')
125
226
  .attr('x1', gx + x.bandwidth() / 2)
126
227
  .attr('x2', gx + x.bandwidth() / 2)
127
- .attr('y1', y(d[this.minKey]))
128
- .attr('y2', y(d[this.maxKey]))
228
+ .attr('y1', currentY(actualMin))
229
+ .attr('y2', currentY(actualMax))
129
230
  .attr('stroke', '#333')
130
- // min/max
231
+
232
+ // min/max 선 (fence 내부)
131
233
  g.append('line')
132
234
  .attr('x1', gx + x.bandwidth() / 4)
133
235
  .attr('x2', gx + (x.bandwidth() * 3) / 4)
134
- .attr('y1', y(d[this.minKey]))
135
- .attr('y2', y(d[this.minKey]))
236
+ .attr('y1', currentY(actualMin))
237
+ .attr('y2', currentY(actualMin))
136
238
  .attr('stroke', '#333')
137
239
  g.append('line')
138
240
  .attr('x1', gx + x.bandwidth() / 4)
139
241
  .attr('x2', gx + (x.bandwidth() * 3) / 4)
140
- .attr('y1', y(d[this.maxKey]))
141
- .attr('y2', y(d[this.maxKey]))
242
+ .attr('y1', currentY(actualMax))
243
+ .attr('y2', currentY(actualMax))
142
244
  .attr('stroke', '#333')
245
+
246
+ // Outlier 표시 (fence 외부의 값들)
247
+ if (groupData[this.minKey] < lowerFence) {
248
+ g.append('circle')
249
+ .attr('cx', gx + x.bandwidth() / 2)
250
+ .attr('cy', currentY(groupData[this.minKey]))
251
+ .attr('r', 3)
252
+ .attr('fill', '#ff4444')
253
+ .attr('stroke', '#333')
254
+ .attr('stroke-width', 1)
255
+ }
256
+ if (groupData[this.maxKey] > upperFence) {
257
+ g.append('circle')
258
+ .attr('cx', gx + x.bandwidth() / 2)
259
+ .attr('cy', currentY(groupData[this.maxKey]))
260
+ .attr('r', 3)
261
+ .attr('fill', '#ff4444')
262
+ .attr('stroke', '#333')
263
+ .attr('stroke-width', 1)
264
+ }
265
+
143
266
  // 평균값
144
267
  g.append('circle')
145
268
  .attr('cx', gx + x.bandwidth() / 2)
146
- .attr('cy', y(d[this.meanKey]))
269
+ .attr('cy', currentY(groupData[this.meanKey]))
147
270
  .attr('r', 4)
148
271
  .attr('fill', 'orange')
149
272
  })
150
273
  // 현재 그룹 값 강조
151
- this.data.forEach(d => {
152
- if (d.group === this.currentGroup) {
274
+ if (this.currentGroup && groups.includes(this.currentGroup)) {
275
+ const currentGroupData = this.data.find(d => this.getOrgValue(d) === this.currentGroup)
276
+ if (currentGroupData) {
277
+ let currentY = y
278
+ if (this.independentScale) {
279
+ const values = [
280
+ currentGroupData[this.minKey],
281
+ currentGroupData[this.maxKey],
282
+ currentGroupData[this.q1Key],
283
+ currentGroupData[this.q3Key],
284
+ currentGroupData[this.medianKey],
285
+ currentGroupData[this.meanKey],
286
+ currentGroupData[this.valueKey]
287
+ ]
288
+ const min = d3.min(values) ?? 0
289
+ const max = d3.max(values) ?? 1
290
+ currentY = d3.scaleLinear().domain([min, max]).nice().range([plotH, 0])
291
+ }
292
+
153
293
  g.append('circle')
154
- .attr('cx', x(d.group) + x.bandwidth() / 2)
155
- .attr('cy', y(d[this.valueKey]))
294
+ .attr('cx', x(this.currentGroup) + x.bandwidth() / 2)
295
+ .attr('cy', currentY(currentGroupData[this.valueKey]))
156
296
  .attr('r', 6)
157
297
  .attr('fill', '#e91e63')
158
298
  .attr('stroke', '#fff')
159
299
  .attr('stroke-width', 2)
160
300
  }
161
- })
301
+ }
162
302
  }
163
303
  }
@@ -66,11 +66,11 @@ export class KpiRadarChart extends LitElement {
66
66
  const r = Math.min(w, h) / 2 - 40
67
67
  const angleSlice = (2 * Math.PI) / (this.categories.length || 1)
68
68
 
69
- // 데이터 변환: { group, values: [ {category, value} ... ] }
70
- const groupData = d3
71
- .groups(this.data, d => d.group)
72
- .map(([group, values]) => ({
73
- group,
69
+ // 데이터 변환: { org, values: [ {category, value} ... ] }
70
+ const orgData = d3
71
+ .groups(this.data, d => d.org)
72
+ .map(([org, values]) => ({
73
+ org,
74
74
  values: this.categories.map((cat, i) => {
75
75
  const found = values.find(v => v.category === cat)
76
76
  return { category: cat, value: found ? found[this.valueKey] : 0 }
@@ -110,7 +110,7 @@ export class KpiRadarChart extends LitElement {
110
110
  })
111
111
 
112
112
  // 그룹별 폴리곤
113
- groupData.forEach(gd => {
113
+ orgData.forEach(gd => {
114
114
  // 마지막에 첫 점을 한 번 더 추가
115
115
  const closedValues = [...gd.values, gd.values[0]]
116
116
  const line = d3
@@ -120,9 +120,9 @@ export class KpiRadarChart extends LitElement {
120
120
  g.append('path')
121
121
  .datum(closedValues)
122
122
  .attr('d', line as any)
123
- .attr('fill', gd.group === this.currentGroup ? 'rgba(33,150,243,0.4)' : 'rgba(200,200,200,0.2)')
124
- .attr('stroke', gd.group === this.currentGroup ? '#2196f3' : '#aaa')
125
- .attr('stroke-width', gd.group === this.currentGroup ? 3 : 1)
123
+ .attr('fill', gd.org === this.currentGroup ? 'rgba(33,150,243,0.4)' : 'rgba(200,200,200,0.2)')
124
+ .attr('stroke', gd.org === this.currentGroup ? '#2196f3' : '#aaa')
125
+ .attr('stroke-width', gd.org === this.currentGroup ? 3 : 1)
126
126
  })
127
127
  }
128
128
  }
@@ -17,6 +17,7 @@ import { i18next, localize } from '@operato/i18n'
17
17
  import { notify, openPopup } from '@operato/layout'
18
18
  import { OxPopup, OxPrompt } from '@operato/popup'
19
19
  import { isMobileDevice } from '@operato/utils'
20
+ import { p13n } from '@operato/p13n'
20
21
 
21
22
  import { connect } from 'pwa-helpers/connect-mixin'
22
23
  import gql from 'graphql-tag'
@@ -26,7 +27,7 @@ import { KpiGradeEditor } from './kpi-grade-editor'
26
27
  import { KpiVizEditor } from './kpi-viz-editor'
27
28
 
28
29
  @customElement('kpi-list-page')
29
- export class KpiListPage extends connect(store)(localize(i18next)(ScopedElementsMixin(PageView))) {
30
+ export class KpiListPage extends connect(store)(p13n(localize(i18next)(ScopedElementsMixin(PageView)))) {
30
31
  static styles = [
31
32
  ScrollbarStyles,
32
33
  CommonGristStyles,
@@ -67,32 +68,65 @@ export class KpiListPage extends connect(store)(localize(i18next)(ScopedElements
67
68
 
68
69
  @state() availableVariables: any[] = []
69
70
  @state() availableVariablesLoaded = false
71
+ @state() hierarchicalView = false
70
72
 
71
- async getAvailableKpiMetricVariables() {
72
- if (this.availableVariablesLoaded) {
73
+ async getAvailableKpiMetricVariables(currentKpi?: any) {
74
+ // Leaf KPI인 경우: kpi-metric을 변수로 사용
75
+ if (!currentKpi || currentKpi.isLeaf) {
76
+ if (this.availableVariablesLoaded) {
77
+ return this.availableVariables
78
+ }
79
+ const response = await client.query({
80
+ query: gql`
81
+ query {
82
+ kpiMetrics {
83
+ items {
84
+ name
85
+ description
86
+ unit
87
+ }
88
+ }
89
+ }
90
+ `
91
+ })
92
+ this.availableVariables = (response.data.kpiMetrics.items || []).map(metric => ({
93
+ name: metric.name,
94
+ description: metric.description,
95
+ type: 'kpi-metric',
96
+ unit: metric.unit
97
+ }))
98
+ this.availableVariablesLoaded = true
73
99
  return this.availableVariables
74
100
  }
75
- const response = await client.query({
76
- query: gql`
77
- query {
78
- kpiMetrics {
79
- items {
80
- name
81
- description
82
- unit
101
+
102
+ // 부모 KPI인 경우: 자식 KPI를 변수로 사용
103
+ try {
104
+ const response = await client.query({
105
+ query: gql`
106
+ query ($id: String!) {
107
+ kpi(id: $id) {
108
+ children {
109
+ id
110
+ name
111
+ description
112
+ }
83
113
  }
84
114
  }
115
+ `,
116
+ variables: {
117
+ id: currentKpi.id
85
118
  }
86
- `
87
- })
88
- this.availableVariables = (response.data.kpiMetrics.items || []).map(metric => ({
89
- name: metric.name,
90
- description: metric.description,
91
- type: 'kpi-metric',
92
- unit: metric.unit
93
- }))
94
- this.availableVariablesLoaded = true
95
- return this.availableVariables
119
+ })
120
+
121
+ return (response.data.kpi.children || []).map(childKpi => ({
122
+ name: childKpi.name,
123
+ description: childKpi.description,
124
+ type: 'child-kpi'
125
+ }))
126
+ } catch (error) {
127
+ console.error('Failed to fetch child KPIs:', error)
128
+ return []
129
+ }
96
130
  }
97
131
 
98
132
  get context() {
@@ -111,6 +145,11 @@ export class KpiListPage extends connect(store)(localize(i18next)(ScopedElements
111
145
  },
112
146
  help: 'kpi/kpi',
113
147
  actions: [
148
+ {
149
+ title: this.hierarchicalView ? 'List View' : 'Tree View',
150
+ action: this._toggleHierarchicalView.bind(this),
151
+ icon: this.hierarchicalView ? 'list' : 'account_tree'
152
+ },
114
153
  {
115
154
  title: i18next.t('button.save'),
116
155
  action: this._updateKpi.bind(this),
@@ -136,7 +175,12 @@ export class KpiListPage extends connect(store)(localize(i18next)(ScopedElements
136
175
  const mode = this.mode || (isMobileDevice() ? 'CARD' : 'GRID')
137
176
 
138
177
  return html`
139
- <ox-grist .mode=${mode} .config=${this.gristConfig} .fetchHandler=${this.fetchHandler.bind(this)}>
178
+ <ox-grist
179
+ .mode=${mode}
180
+ .config=${this.gristConfig}
181
+ .fetchHandler=${this.fetchHandler.bind(this)}
182
+ .personalConfigProvider=${this.getPagePreferenceProvider('ox-grist')!}
183
+ >
140
184
  <div slot="headroom" class="header">
141
185
  <div class="filters">
142
186
  <ox-filters-form autofocus without-search></ox-filters-form>
@@ -154,6 +198,8 @@ export class KpiListPage extends connect(store)(localize(i18next)(ScopedElements
154
198
  </ox-record-creator>
155
199
  </div>
156
200
  </div>
201
+
202
+ <ox-grist-personalizer slot="setting"></ox-grist-personalizer>
157
203
  </ox-grist>
158
204
  `
159
205
  }
@@ -170,7 +216,6 @@ export class KpiListPage extends connect(store)(localize(i18next)(ScopedElements
170
216
  details: [
171
217
  'name',
172
218
  'description',
173
- 'category',
174
219
  'formula',
175
220
  'active',
176
221
  'state',
@@ -220,26 +265,36 @@ export class KpiListPage extends connect(store)(localize(i18next)(ScopedElements
220
265
  },
221
266
  {
222
267
  type: 'resource-object',
223
- name: 'category',
268
+ name: 'parent',
224
269
  label: true,
225
- header: '카테고리',
270
+ header: '상위 KPI',
226
271
  record: {
227
272
  editable: true,
228
273
  options: {
229
- title: i18next.t('title.lookup category'),
230
- queryName: 'kpiCategories'
274
+ title: i18next.t('title.lookup KPI'),
275
+ queryName: 'kpis',
276
+ basicArgs: {
277
+ filters: [{ name: 'isLeaf', operator: 'eq', value: false }]
278
+ }
231
279
  }
232
280
  },
233
281
  width: 200
234
282
  },
283
+ {
284
+ type: 'boolean',
285
+ name: 'isLeaf',
286
+ header: '리프 KPI',
287
+ record: { editable: true },
288
+ width: 100
289
+ },
235
290
  {
236
291
  type: 'formula',
237
292
  name: 'formula',
238
293
  header: '산식',
239
294
  record: {
240
295
  editable: true,
241
- availableVariables: async () => {
242
- return await this.getAvailableKpiMetricVariables()
296
+ availableVariables: async (value: string, column: ColumnConfig, record: any) => {
297
+ return await this.getAvailableKpiMetricVariables(record)
243
298
  }
244
299
  },
245
300
  width: 320
@@ -490,6 +545,10 @@ export class KpiListPage extends connect(store)(localize(i18next)(ScopedElements
490
545
  }
491
546
 
492
547
  async fetchHandler({ page = 1, limit = 100, sortings = [], filters = [] }: FetchOption) {
548
+ if (this.hierarchicalView) {
549
+ return this.fetchHierarchicalData()
550
+ }
551
+
493
552
  const response = await client.query({
494
553
  query: gql`
495
554
  query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {
@@ -510,11 +569,15 @@ export class KpiListPage extends connect(store)(localize(i18next)(ScopedElements
510
569
  scheduleId
511
570
  timezone
512
571
  version
513
- category {
572
+ parent {
573
+ id
574
+ name
575
+ }
576
+ children {
514
577
  id
515
578
  name
516
- description
517
579
  }
580
+ isLeaf
518
581
  updater {
519
582
  id
520
583
  name
@@ -790,7 +853,7 @@ export class KpiListPage extends connect(store)(localize(i18next)(ScopedElements
790
853
  id
791
854
  value
792
855
  valueDate
793
- group
856
+ org
794
857
  }
795
858
  }
796
859
  `,
@@ -806,4 +869,105 @@ export class KpiListPage extends connect(store)(localize(i18next)(ScopedElements
806
869
  notify({ message: 'KPI 실적값 계산 중 오류가 발생했습니다.' })
807
870
  }
808
871
  }
872
+
873
+ _toggleHierarchicalView() {
874
+ this.hierarchicalView = !this.hierarchicalView
875
+ this.grist.fetch()
876
+ }
877
+
878
+ async fetchHierarchicalData() {
879
+ const response = await client.query({
880
+ query: gql`
881
+ query {
882
+ kpiTree {
883
+ id
884
+ name
885
+ description
886
+ active
887
+ formula
888
+ periodType
889
+ scoreFormula
890
+ grades
891
+ vizType
892
+ vizMeta
893
+ weight
894
+ schedule
895
+ scheduleId
896
+ timezone
897
+ version
898
+ parent {
899
+ id
900
+ name
901
+ }
902
+ children {
903
+ id
904
+ name
905
+ description
906
+ active
907
+ isLeaf
908
+ weight
909
+ children {
910
+ id
911
+ name
912
+ description
913
+ active
914
+ isLeaf
915
+ weight
916
+ children {
917
+ id
918
+ name
919
+ description
920
+ active
921
+ isLeaf
922
+ weight
923
+ }
924
+ }
925
+ }
926
+ isLeaf
927
+ updater {
928
+ id
929
+ name
930
+ }
931
+ updatedAt
932
+ creator {
933
+ id
934
+ name
935
+ }
936
+ createdAt
937
+ }
938
+ }
939
+ `
940
+ })
941
+
942
+ const flattenedRecords = this.flattenTreeData(response.data.kpiTree)
943
+
944
+ return {
945
+ total: flattenedRecords.length,
946
+ records: flattenedRecords
947
+ }
948
+ }
949
+
950
+ flattenTreeData(treeData: any[], level = 0): any[] {
951
+ const flattened: any[] = []
952
+
953
+ for (const item of treeData) {
954
+ const flattenedItem = {
955
+ ...item,
956
+ __level: level,
957
+ __hasChildren: item.children && item.children.length > 0,
958
+ __expanded: true
959
+ }
960
+
961
+ // Add indentation to name for visual hierarchy
962
+ flattenedItem.name = ' '.repeat(level) + (level > 0 ? '└ ' : '') + item.name
963
+
964
+ flattened.push(flattenedItem)
965
+
966
+ if (item.children && item.children.length > 0) {
967
+ flattened.push(...this.flattenTreeData(item.children, level + 1))
968
+ }
969
+ }
970
+
971
+ return flattened
972
+ }
809
973
  }