@things-factory/kpi 1.0.0-alpha.5 → 9.0.9

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 (309) hide show
  1. package/README.md +74 -0
  2. package/client/pages/kpi/kpi-list-page.ts +97 -47
  3. package/client/pages/kpi/kpi-overview-sample.ts +260 -0
  4. package/client/pages/kpi/kpi-overview.ts +271 -0
  5. package/client/pages/kpi-category/kpi-category-list-page.ts +39 -50
  6. package/client/pages/kpi-dashboard/kpi-alert-panel.ts +114 -0
  7. package/client/pages/kpi-dashboard/kpi-dashboard.ts +285 -0
  8. package/client/pages/kpi-dashboard/kpi-grade-visualization.ts +75 -0
  9. package/client/pages/kpi-dashboard/kpi-history-viewer.ts +58 -0
  10. package/client/pages/kpi-dashboard/kpi-list-summary.ts +100 -0
  11. package/client/pages/kpi-dashboard/kpi-performance-summary.ts +124 -0
  12. package/client/pages/kpi-dashboard/kpi-value-entry.ts +78 -0
  13. package/client/pages/kpi-grade/kpi-grade-list-page.ts +58 -68
  14. package/client/pages/kpi-history/kpi-history-list-page.ts +146 -0
  15. package/client/pages/kpi-metric/kpi-metric-list-page.ts +67 -63
  16. package/client/pages/kpi-value/kpi-value-list-page.ts +73 -72
  17. package/client/pages/kpi-value/kpi-value-manual-entry-form.ts +168 -0
  18. package/client/pages/kpi-value/kpi-value-manual-entry-page.ts +173 -0
  19. package/client/route.ts +35 -0
  20. package/dist-client/pages/kpi/kpi-importer.js +16 -17
  21. package/dist-client/pages/kpi/kpi-importer.js.map +1 -1
  22. package/dist-client/pages/kpi/kpi-list-page.js +128 -68
  23. package/dist-client/pages/kpi/kpi-list-page.js.map +1 -1
  24. package/dist-client/pages/kpi/kpi-overview-sample.d.ts +36 -0
  25. package/dist-client/pages/kpi/kpi-overview-sample.js +264 -0
  26. package/dist-client/pages/kpi/kpi-overview-sample.js.map +1 -0
  27. package/dist-client/pages/kpi/kpi-overview.d.ts +14 -0
  28. package/dist-client/pages/kpi/kpi-overview.js +290 -0
  29. package/dist-client/pages/kpi/kpi-overview.js.map +1 -0
  30. package/dist-client/pages/kpi-category/kpi-category-importer.js +16 -17
  31. package/dist-client/pages/kpi-category/kpi-category-importer.js.map +1 -1
  32. package/dist-client/pages/kpi-category/kpi-category-list-page.js +70 -71
  33. package/dist-client/pages/kpi-category/kpi-category-list-page.js.map +1 -1
  34. package/dist-client/pages/kpi-dashboard/kpi-alert-panel.d.ts +18 -0
  35. package/dist-client/pages/kpi-dashboard/kpi-alert-panel.js +128 -0
  36. package/dist-client/pages/kpi-dashboard/kpi-alert-panel.js.map +1 -0
  37. package/dist-client/pages/kpi-dashboard/kpi-dashboard.d.ts +24 -0
  38. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js +305 -0
  39. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js.map +1 -0
  40. package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.d.ts +12 -0
  41. package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.js +82 -0
  42. package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.js.map +1 -0
  43. package/dist-client/pages/kpi-dashboard/kpi-history-viewer.d.ts +11 -0
  44. package/dist-client/pages/kpi-dashboard/kpi-history-viewer.js +65 -0
  45. package/dist-client/pages/kpi-dashboard/kpi-history-viewer.js.map +1 -0
  46. package/dist-client/pages/kpi-dashboard/kpi-list-summary.d.ts +13 -0
  47. package/dist-client/pages/kpi-dashboard/kpi-list-summary.js +115 -0
  48. package/dist-client/pages/kpi-dashboard/kpi-list-summary.js.map +1 -0
  49. package/dist-client/pages/kpi-dashboard/kpi-performance-summary.d.ts +15 -0
  50. package/dist-client/pages/kpi-dashboard/kpi-performance-summary.js +139 -0
  51. package/dist-client/pages/kpi-dashboard/kpi-performance-summary.js.map +1 -0
  52. package/dist-client/pages/kpi-dashboard/kpi-value-entry.d.ts +7 -0
  53. package/dist-client/pages/kpi-dashboard/kpi-value-entry.js +86 -0
  54. package/dist-client/pages/kpi-dashboard/kpi-value-entry.js.map +1 -0
  55. package/dist-client/pages/kpi-grade/kpi-grade-importer.js +16 -17
  56. package/dist-client/pages/kpi-grade/kpi-grade-importer.js.map +1 -1
  57. package/dist-client/pages/kpi-grade/kpi-grade-list-page.js +89 -89
  58. package/dist-client/pages/kpi-grade/kpi-grade-list-page.js.map +1 -1
  59. package/dist-client/pages/kpi-history/kpi-history-list-page.d.ts +16 -0
  60. package/dist-client/pages/kpi-history/kpi-history-list-page.js +155 -0
  61. package/dist-client/pages/kpi-history/kpi-history-list-page.js.map +1 -0
  62. package/dist-client/pages/kpi-metric/kpi-metric-importer.js +16 -17
  63. package/dist-client/pages/kpi-metric/kpi-metric-importer.js.map +1 -1
  64. package/dist-client/pages/kpi-metric/kpi-metric-list-page.js +98 -84
  65. package/dist-client/pages/kpi-metric/kpi-metric-list-page.js.map +1 -1
  66. package/dist-client/pages/kpi-value/kpi-value-importer.js +16 -17
  67. package/dist-client/pages/kpi-value/kpi-value-importer.js.map +1 -1
  68. package/dist-client/pages/kpi-value/kpi-value-list-page.js +104 -93
  69. package/dist-client/pages/kpi-value/kpi-value-list-page.js.map +1 -1
  70. package/dist-client/pages/kpi-value/kpi-value-manual-entry-form.d.ts +14 -0
  71. package/dist-client/pages/kpi-value/kpi-value-manual-entry-form.js +201 -0
  72. package/dist-client/pages/kpi-value/kpi-value-manual-entry-form.js.map +1 -0
  73. package/dist-client/pages/kpi-value/kpi-value-manual-entry-page.d.ts +41 -0
  74. package/dist-client/pages/kpi-value/kpi-value-manual-entry-page.js +180 -0
  75. package/dist-client/pages/kpi-value/kpi-value-manual-entry-page.js.map +1 -0
  76. package/dist-client/route.d.ts +1 -1
  77. package/dist-client/route.js +27 -0
  78. package/dist-client/route.js.map +1 -1
  79. package/dist-client/tsconfig.tsbuildinfo +1 -1
  80. package/dist-server/index.d.ts +2 -1
  81. package/dist-server/index.js +2 -1
  82. package/dist-server/index.js.map +1 -1
  83. package/dist-server/migrations/1752188906708-SeedKpiCategory.d.ts +5 -0
  84. package/dist-server/migrations/1752188906708-SeedKpiCategory.js +56 -0
  85. package/dist-server/migrations/1752188906708-SeedKpiCategory.js.map +1 -0
  86. package/dist-server/migrations/1752190849681-SeedKpi.d.ts +5 -0
  87. package/dist-server/migrations/1752190849681-SeedKpi.js +107 -0
  88. package/dist-server/migrations/1752190849681-SeedKpi.js.map +1 -0
  89. package/dist-server/migrations/1752191090459-SeedKpiGrade.d.ts +5 -0
  90. package/dist-server/migrations/1752191090459-SeedKpiGrade.js +271 -0
  91. package/dist-server/migrations/1752191090459-SeedKpiGrade.js.map +1 -0
  92. package/dist-server/migrations/index.d.ts +1 -0
  93. package/dist-server/migrations/index.js +12 -0
  94. package/dist-server/migrations/index.js.map +1 -0
  95. package/dist-server/routes.d.ts +1 -0
  96. package/dist-server/routes.js +48 -0
  97. package/dist-server/routes.js.map +1 -1
  98. package/dist-server/service/index.d.ts +3 -4
  99. package/dist-server/service/index.js +4 -6
  100. package/dist-server/service/index.js.map +1 -1
  101. package/dist-server/service/kpi/aggregate-kpi.d.ts +8 -0
  102. package/dist-server/service/kpi/aggregate-kpi.js +88 -0
  103. package/dist-server/service/kpi/aggregate-kpi.js.map +1 -0
  104. package/dist-server/service/kpi/event-subscriber.d.ts +2 -0
  105. package/dist-server/service/kpi/event-subscriber.js +10 -0
  106. package/dist-server/service/kpi/event-subscriber.js.map +1 -1
  107. package/dist-server/service/kpi/index.d.ts +2 -1
  108. package/dist-server/service/kpi/kpi-formula.service.d.ts +24 -0
  109. package/dist-server/service/kpi/kpi-formula.service.js +64 -0
  110. package/dist-server/service/kpi/kpi-formula.service.js.map +1 -0
  111. package/dist-server/service/kpi/kpi-history.d.ts +6 -2
  112. package/dist-server/service/kpi/kpi-history.js +29 -11
  113. package/dist-server/service/kpi/kpi-history.js.map +1 -1
  114. package/dist-server/service/kpi/kpi-mutation.d.ts +2 -0
  115. package/dist-server/service/kpi/kpi-mutation.js +215 -10
  116. package/dist-server/service/kpi/kpi-mutation.js.map +1 -1
  117. package/dist-server/service/kpi/kpi-query.d.ts +10 -0
  118. package/dist-server/service/kpi/kpi-query.js +87 -3
  119. package/dist-server/service/kpi/kpi-query.js.map +1 -1
  120. package/dist-server/service/kpi/kpi-type.d.ts +14 -3
  121. package/dist-server/service/kpi/kpi-type.js +81 -18
  122. package/dist-server/service/kpi/kpi-type.js.map +1 -1
  123. package/dist-server/service/kpi/kpi.d.ts +14 -3
  124. package/dist-server/service/kpi/kpi.js +96 -19
  125. package/dist-server/service/kpi/kpi.js.map +1 -1
  126. package/dist-server/service/kpi-alert/index.d.ts +2 -0
  127. package/dist-server/service/kpi-alert/index.js +6 -0
  128. package/dist-server/service/kpi-alert/index.js.map +1 -0
  129. package/dist-server/service/kpi-alert/kpi-alert-query.d.ts +4 -0
  130. package/dist-server/service/kpi-alert/kpi-alert-query.js +71 -0
  131. package/dist-server/service/kpi-alert/kpi-alert-query.js.map +1 -0
  132. package/dist-server/service/kpi-alert/kpi-alert-type.d.ts +8 -0
  133. package/dist-server/service/kpi-alert/kpi-alert-type.js +33 -0
  134. package/dist-server/service/kpi-alert/kpi-alert-type.js.map +1 -0
  135. package/dist-server/service/kpi-category/kpi-category-mutation.d.ts +1 -1
  136. package/dist-server/service/kpi-category/kpi-category-mutation.js +36 -10
  137. package/dist-server/service/kpi-category/kpi-category-mutation.js.map +1 -1
  138. package/dist-server/service/kpi-category/kpi-category-query.d.ts +2 -0
  139. package/dist-server/service/kpi-category/kpi-category-query.js +18 -2
  140. package/dist-server/service/kpi-category/kpi-category-query.js.map +1 -1
  141. package/dist-server/service/kpi-category/kpi-category-type.d.ts +3 -5
  142. package/dist-server/service/kpi-category/kpi-category-type.js +16 -20
  143. package/dist-server/service/kpi-category/kpi-category-type.js.map +1 -1
  144. package/dist-server/service/kpi-category/kpi-category.d.ts +7 -9
  145. package/dist-server/service/kpi-category/kpi-category.js +40 -38
  146. package/dist-server/service/kpi-category/kpi-category.js.map +1 -1
  147. package/dist-server/service/kpi-grade/index.d.ts +1 -2
  148. package/dist-server/service/kpi-grade/index.js +2 -4
  149. package/dist-server/service/kpi-grade/index.js.map +1 -1
  150. package/dist-server/service/kpi-grade/kpi-grade-mutation.d.ts +1 -1
  151. package/dist-server/service/kpi-grade/kpi-grade-mutation.js +33 -10
  152. package/dist-server/service/kpi-grade/kpi-grade-mutation.js.map +1 -1
  153. package/dist-server/service/kpi-grade/kpi-grade-query.js +2 -2
  154. package/dist-server/service/kpi-grade/kpi-grade-query.js.map +1 -1
  155. package/dist-server/service/kpi-grade/kpi-grade-type.d.ts +13 -4
  156. package/dist-server/service/kpi-grade/kpi-grade-type.js +54 -18
  157. package/dist-server/service/kpi-grade/kpi-grade-type.js.map +1 -1
  158. package/dist-server/service/kpi-grade/kpi-grade.d.ts +8 -8
  159. package/dist-server/service/kpi-grade/kpi-grade.js +48 -40
  160. package/dist-server/service/kpi-grade/kpi-grade.js.map +1 -1
  161. package/dist-server/service/kpi-metric/aggregate-kpi-metric.d.ts +8 -0
  162. package/dist-server/service/kpi-metric/aggregate-kpi-metric.js +134 -0
  163. package/dist-server/service/kpi-metric/aggregate-kpi-metric.js.map +1 -0
  164. package/dist-server/service/kpi-metric/index.d.ts +1 -2
  165. package/dist-server/service/kpi-metric/index.js +2 -4
  166. package/dist-server/service/kpi-metric/index.js.map +1 -1
  167. package/dist-server/service/kpi-metric/kpi-metric-mutation.d.ts +1 -1
  168. package/dist-server/service/kpi-metric/kpi-metric-mutation.js +139 -13
  169. package/dist-server/service/kpi-metric/kpi-metric-mutation.js.map +1 -1
  170. package/dist-server/service/kpi-metric/kpi-metric-query.js +3 -3
  171. package/dist-server/service/kpi-metric/kpi-metric-query.js.map +1 -1
  172. package/dist-server/service/kpi-metric/kpi-metric-type.d.ts +17 -4
  173. package/dist-server/service/kpi-metric/kpi-metric-type.js +69 -11
  174. package/dist-server/service/kpi-metric/kpi-metric-type.js.map +1 -1
  175. package/dist-server/service/kpi-metric/kpi-metric.d.ts +11 -8
  176. package/dist-server/service/kpi-metric/kpi-metric.js +62 -37
  177. package/dist-server/service/kpi-metric/kpi-metric.js.map +1 -1
  178. package/dist-server/service/kpi-value/kpi-value-mutation.js +66 -11
  179. package/dist-server/service/kpi-value/kpi-value-mutation.js.map +1 -1
  180. package/dist-server/service/kpi-value/kpi-value-query.d.ts +2 -0
  181. package/dist-server/service/kpi-value/kpi-value-query.js +15 -2
  182. package/dist-server/service/kpi-value/kpi-value-query.js.map +1 -1
  183. package/dist-server/service/kpi-value/kpi-value-type.d.ts +19 -10
  184. package/dist-server/service/kpi-value/kpi-value-type.js +87 -24
  185. package/dist-server/service/kpi-value/kpi-value-type.js.map +1 -1
  186. package/dist-server/service/kpi-value/kpi-value.d.ts +15 -9
  187. package/dist-server/service/kpi-value/kpi-value.js +86 -32
  188. package/dist-server/service/kpi-value/kpi-value.js.map +1 -1
  189. package/dist-server/tsconfig.tsbuildinfo +1 -1
  190. package/implement-plan/01-entity-type-resolver.md +57 -0
  191. package/implement-plan/02-dataset-integration.md +35 -0
  192. package/implement-plan/03-kpivalue-automation.md +34 -0
  193. package/implement-plan/04-version-history.md +33 -0
  194. package/implement-plan/05-grade-visualization.md +33 -0
  195. package/implement-plan/06-graphql-frontend.md +30 -0
  196. package/implement-plan/07-test-docs.md +35 -0
  197. package/implement-plan/TODO.md +41 -0
  198. package/package.json +18 -6
  199. package/server/index.ts +2 -1
  200. package/server/migrations/1752188906708-SeedKpiCategory.ts +61 -0
  201. package/server/migrations/1752190849681-SeedKpi.ts +112 -0
  202. package/server/migrations/1752191090459-SeedKpiGrade.ts +270 -0
  203. package/server/migrations/index.ts +9 -0
  204. package/server/routes.ts +55 -0
  205. package/server/service/index.ts +4 -6
  206. package/server/service/kpi/aggregate-kpi.ts +82 -0
  207. package/server/service/kpi/event-subscriber.ts +12 -0
  208. package/server/service/kpi/kpi-formula.service.ts +63 -0
  209. package/server/service/kpi/kpi-history.ts +29 -21
  210. package/server/service/kpi/kpi-mutation.ts +194 -12
  211. package/server/service/kpi/kpi-query.ts +57 -2
  212. package/server/service/kpi/kpi-type.ts +72 -19
  213. package/server/service/kpi/kpi.ts +96 -20
  214. package/server/service/kpi-alert/index.ts +3 -0
  215. package/server/service/kpi-alert/kpi-alert-query.ts +59 -0
  216. package/server/service/kpi-alert/kpi-alert-type.ts +20 -0
  217. package/server/service/kpi-category/kpi-category-mutation.ts +17 -4
  218. package/server/service/kpi-category/kpi-category-query.ts +20 -3
  219. package/server/service/kpi-category/kpi-category-type.ts +17 -19
  220. package/server/service/kpi-category/kpi-category.ts +39 -37
  221. package/server/service/kpi-grade/index.ts +2 -4
  222. package/server/service/kpi-grade/kpi-grade-mutation.ts +13 -4
  223. package/server/service/kpi-grade/kpi-grade-query.ts +5 -2
  224. package/server/service/kpi-grade/kpi-grade-type.ts +46 -19
  225. package/server/service/kpi-grade/kpi-grade.ts +44 -38
  226. package/server/service/kpi-metric/aggregate-kpi-metric.ts +128 -0
  227. package/server/service/kpi-metric/index.ts +2 -4
  228. package/server/service/kpi-metric/kpi-metric-mutation.ts +123 -7
  229. package/server/service/kpi-metric/kpi-metric-query.ts +9 -3
  230. package/server/service/kpi-metric/kpi-metric-type.ts +56 -13
  231. package/server/service/kpi-metric/kpi-metric.ts +55 -35
  232. package/server/service/kpi-value/kpi-value-mutation.ts +52 -14
  233. package/server/service/kpi-value/kpi-value-query.ts +12 -2
  234. package/server/service/kpi-value/kpi-value-type.ts +80 -25
  235. package/server/service/kpi-value/kpi-value.ts +81 -29
  236. package/things-factory.config.js +12 -4
  237. package/translations/en.json +12 -1
  238. package/translations/ja.json +12 -1
  239. package/translations/ko.json +12 -1
  240. package/translations/ms.json +12 -1
  241. package/translations/zh.json +12 -1
  242. package/client/pages/kpe-metric/kpe-metric-importer.ts +0 -90
  243. package/client/pages/kpe-metric/kpe-metric-list-page.ts +0 -398
  244. package/client/pages/kpi-formula/kpi-formula-importer.ts +0 -90
  245. package/client/pages/kpi-formula/kpi-formula-list-page.ts +0 -398
  246. package/client/pages/metric/metric-importer.ts +0 -90
  247. package/client/pages/metric/metric-list-page.ts +0 -398
  248. package/dist-client/pages/kpe-metric/kpe-metric-importer.d.ts +0 -23
  249. package/dist-client/pages/kpe-metric/kpe-metric-importer.js +0 -93
  250. package/dist-client/pages/kpe-metric/kpe-metric-importer.js.map +0 -1
  251. package/dist-client/pages/kpe-metric/kpe-metric-list-page.d.ts +0 -66
  252. package/dist-client/pages/kpe-metric/kpe-metric-list-page.js +0 -370
  253. package/dist-client/pages/kpe-metric/kpe-metric-list-page.js.map +0 -1
  254. package/dist-client/pages/kpi-formula/kpi-formula-importer.d.ts +0 -23
  255. package/dist-client/pages/kpi-formula/kpi-formula-importer.js +0 -93
  256. package/dist-client/pages/kpi-formula/kpi-formula-importer.js.map +0 -1
  257. package/dist-client/pages/kpi-formula/kpi-formula-list-page.d.ts +0 -66
  258. package/dist-client/pages/kpi-formula/kpi-formula-list-page.js +0 -370
  259. package/dist-client/pages/kpi-formula/kpi-formula-list-page.js.map +0 -1
  260. package/dist-client/pages/metric/metric-importer.d.ts +0 -23
  261. package/dist-client/pages/metric/metric-importer.js +0 -93
  262. package/dist-client/pages/metric/metric-importer.js.map +0 -1
  263. package/dist-client/pages/metric/metric-list-page.d.ts +0 -66
  264. package/dist-client/pages/metric/metric-list-page.js +0 -370
  265. package/dist-client/pages/metric/metric-list-page.js.map +0 -1
  266. package/dist-server/service/kpi-formula/event-subscriber.d.ts +0 -7
  267. package/dist-server/service/kpi-formula/event-subscriber.js +0 -21
  268. package/dist-server/service/kpi-formula/event-subscriber.js.map +0 -1
  269. package/dist-server/service/kpi-formula/index.d.ts +0 -7
  270. package/dist-server/service/kpi-formula/index.js +0 -12
  271. package/dist-server/service/kpi-formula/index.js.map +0 -1
  272. package/dist-server/service/kpi-formula/kpi-formula-history.d.ts +0 -25
  273. package/dist-server/service/kpi-formula/kpi-formula-history.js +0 -128
  274. package/dist-server/service/kpi-formula/kpi-formula-history.js.map +0 -1
  275. package/dist-server/service/kpi-formula/kpi-formula-mutation.d.ts +0 -10
  276. package/dist-server/service/kpi-formula/kpi-formula-mutation.js +0 -128
  277. package/dist-server/service/kpi-formula/kpi-formula-mutation.js.map +0 -1
  278. package/dist-server/service/kpi-formula/kpi-formula-query.d.ts +0 -11
  279. package/dist-server/service/kpi-formula/kpi-formula-query.js +0 -79
  280. package/dist-server/service/kpi-formula/kpi-formula-query.js.map +0 -1
  281. package/dist-server/service/kpi-formula/kpi-formula-type.d.ts +0 -20
  282. package/dist-server/service/kpi-formula/kpi-formula-type.js +0 -77
  283. package/dist-server/service/kpi-formula/kpi-formula-type.js.map +0 -1
  284. package/dist-server/service/kpi-formula/kpi-formula.d.ts +0 -24
  285. package/dist-server/service/kpi-formula/kpi-formula.js +0 -109
  286. package/dist-server/service/kpi-formula/kpi-formula.js.map +0 -1
  287. package/dist-server/service/kpi-grade/event-subscriber.d.ts +0 -7
  288. package/dist-server/service/kpi-grade/event-subscriber.js +0 -21
  289. package/dist-server/service/kpi-grade/event-subscriber.js.map +0 -1
  290. package/dist-server/service/kpi-grade/kpi-grade-history.d.ts +0 -25
  291. package/dist-server/service/kpi-grade/kpi-grade-history.js +0 -128
  292. package/dist-server/service/kpi-grade/kpi-grade-history.js.map +0 -1
  293. package/dist-server/service/kpi-metric/event-subscriber.d.ts +0 -7
  294. package/dist-server/service/kpi-metric/event-subscriber.js +0 -21
  295. package/dist-server/service/kpi-metric/event-subscriber.js.map +0 -1
  296. package/dist-server/service/kpi-metric/kpi-metric-history.d.ts +0 -25
  297. package/dist-server/service/kpi-metric/kpi-metric-history.js +0 -128
  298. package/dist-server/service/kpi-metric/kpi-metric-history.js.map +0 -1
  299. package/server/service/kpi-formula/event-subscriber.ts +0 -17
  300. package/server/service/kpi-formula/index.ts +0 -9
  301. package/server/service/kpi-formula/kpi-formula-history.ts +0 -116
  302. package/server/service/kpi-formula/kpi-formula-mutation.ts +0 -137
  303. package/server/service/kpi-formula/kpi-formula-query.ts +0 -48
  304. package/server/service/kpi-formula/kpi-formula-type.ts +0 -55
  305. package/server/service/kpi-formula/kpi-formula.ts +0 -95
  306. package/server/service/kpi-grade/event-subscriber.ts +0 -17
  307. package/server/service/kpi-grade/kpi-grade-history.ts +0 -116
  308. package/server/service/kpi-metric/event-subscriber.ts +0 -17
  309. package/server/service/kpi-metric/kpi-metric-history.ts +0 -116
@@ -5,16 +5,28 @@ import { getRepository } from '@things-factory/shell'
5
5
  import { createAttachment, deleteAttachmentsByRef } from '@things-factory/attachment-base'
6
6
  import { Kpi } from './kpi'
7
7
  import { NewKpi, KpiPatch } from './kpi-type'
8
+ import { KpiCategory } from '../kpi-category/kpi-category'
9
+ import { KpiHistory } from './kpi-history'
10
+ import { KpiFormulaService } from './kpi-formula.service'
11
+ import { Application, CallbackBase, registerSchedule, unregisterSchedule } from '@things-factory/scheduler-client'
8
12
 
9
13
  @Resolver(Kpi)
10
14
  export class KpiMutation {
11
15
  @Directive('@transaction')
12
- @Mutation(returns => Kpi, { description: 'To create new Kpi' })
13
- async createKpi(@Arg('kpi') kpi: NewKpi, @Ctx() context: ResolverContext): Promise<Kpi> {
16
+ @Mutation(returns => Kpi, { description: 'Create a new KPI with the provided details.' })
17
+ async createKpi(
18
+ @Arg('kpi', { description: 'Input object containing details for the new KPI.' }) kpi: NewKpi,
19
+ @Ctx() context: ResolverContext
20
+ ): Promise<Kpi> {
14
21
  const { domain, user, tx } = context.state
15
22
 
23
+ let category = kpi.categoryId
24
+ ? await getRepository(KpiCategory).findOne({ where: { id: kpi.categoryId } })
25
+ : undefined
26
+
16
27
  const result = await getRepository(Kpi, tx).save({
17
28
  ...kpi,
29
+ category,
18
30
  domain,
19
31
  creator: user,
20
32
  updater: user
@@ -34,16 +46,57 @@ export class KpiMutation {
34
46
  )
35
47
  }
36
48
 
49
+ if (kpi.formula) {
50
+ const formulaService = new KpiFormulaService()
51
+ const result = await formulaService.validateFormula(kpi.formula)
52
+ if (!result.valid) {
53
+ throw new Error(result.errors.join('\n'))
54
+ }
55
+ }
56
+
57
+ // KPI 스케줄러 등록
58
+ if (kpi.schedule) {
59
+ const handle = await registerSchedule({
60
+ name: result.name,
61
+ client: {
62
+ application: Application,
63
+ group: `${domain.id}`,
64
+ type: 'kpi',
65
+ key: result.id,
66
+ operation: 'schedule'
67
+ },
68
+ type: 'cron',
69
+ schedule: kpi.schedule,
70
+ timezone: kpi.timezone,
71
+ task: {
72
+ type: 'rest',
73
+ connection: {
74
+ host: `${CallbackBase}/callback-schedule-for-kpi`,
75
+ headers: {
76
+ 'Content-Type': 'application/json',
77
+ accept: '*/*'
78
+ }
79
+ },
80
+ data: {
81
+ domainId: domain.id,
82
+ kpiId: result.id
83
+ },
84
+ history_check: true,
85
+ failed_policy: 'retry_dlq',
86
+ max_retry_count: 3,
87
+ retry_period: 60
88
+ }
89
+ })
90
+ result.scheduleId = handle
91
+ await getRepository(Kpi, tx).save(result)
92
+ }
93
+
37
94
  return result
38
95
  }
39
96
 
40
97
  @Directive('@transaction')
41
98
  @Mutation(returns => Kpi, { description: 'To modify Kpi information' })
42
- async updateKpi(
43
- @Arg('id') id: string,
44
- @Arg('patch') patch: KpiPatch,
45
- @Ctx() context: ResolverContext
46
- ): Promise<Kpi> {
99
+ async updateKpi(@Arg('id') id: string, @Arg('patch') patch: KpiPatch, @Ctx() context: ResolverContext): Promise<Kpi> {
47
100
  const { domain, user, tx } = context.state
48
101
 
49
102
  const repository = getRepository(Kpi, tx)
@@ -51,9 +104,14 @@ export class KpiMutation {
51
104
  where: { domain: { id: domain.id }, id }
52
105
  })
53
106
 
107
+ let category = patch.categoryId
108
+ ? await getRepository(KpiCategory).findOne({ where: { id: patch.categoryId } })
109
+ : kpi.category
110
+
54
111
  const result = await repository.save({
55
112
  ...kpi,
56
113
  ...patch,
114
+ category,
57
115
  updater: user
58
116
  })
59
117
 
@@ -72,11 +130,66 @@ export class KpiMutation {
72
130
  )
73
131
  }
74
132
 
133
+ if (patch.formula) {
134
+ const formulaService = new KpiFormulaService()
135
+ const result = await formulaService.validateFormula(patch.formula)
136
+ if (!result.valid) {
137
+ throw new Error(result.errors.join('\n'))
138
+ }
139
+ }
140
+
141
+ // KPI 스케줄러 등록/해제 갱신
142
+ if (patch.schedule) {
143
+ // 기존 스케줄러 해제
144
+ if (kpi.scheduleId) {
145
+ await unregisterSchedule(kpi.scheduleId)
146
+ }
147
+ // 새 스케줄러 등록
148
+ const handle = await registerSchedule({
149
+ name: result.name,
150
+ client: {
151
+ application: Application,
152
+ group: `${domain.id}`,
153
+ type: 'kpi',
154
+ key: result.id,
155
+ operation: 'schedule'
156
+ },
157
+ type: 'cron',
158
+ schedule: patch.schedule,
159
+ timezone: patch.timezone,
160
+ task: {
161
+ type: 'rest',
162
+ connection: {
163
+ host: `${CallbackBase}/callback-schedule-for-kpi`,
164
+ headers: {
165
+ 'Content-Type': 'application/json',
166
+ accept: '*/*'
167
+ }
168
+ },
169
+ data: {
170
+ domainId: domain.id,
171
+ kpiId: result.id
172
+ },
173
+ history_check: true,
174
+ failed_policy: 'retry_dlq',
175
+ max_retry_count: 3,
176
+ retry_period: 60
177
+ }
178
+ })
179
+ result.scheduleId = handle
180
+ await repository.save(result)
181
+ } else if (patch.schedule === null && kpi.scheduleId) {
182
+ // 스케줄 해제 요청
183
+ await unregisterSchedule(kpi.scheduleId)
184
+ result.scheduleId = null
185
+ await repository.save(result)
186
+ }
187
+
75
188
  return result
76
189
  }
77
190
 
78
191
  @Directive('@transaction')
79
- @Mutation(returns => [Kpi], { description: "To modify multiple Kpis' information" })
192
+ @Mutation(returns => Boolean, { description: "To modify multiple Kpis' information" })
80
193
  async updateMultipleKpi(
81
194
  @Arg('patches', type => [KpiPatch]) patches: KpiPatch[],
82
195
  @Ctx() context: ResolverContext
@@ -155,6 +268,11 @@ export class KpiMutation {
155
268
  async deleteKpi(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<boolean> {
156
269
  const { domain, tx } = context.state
157
270
 
271
+ const kpi = await getRepository(Kpi, tx).findOne({ where: { domain: { id: domain.id }, id } })
272
+ if (kpi && kpi.scheduleId) {
273
+ await unregisterSchedule(kpi.scheduleId)
274
+ }
275
+
158
276
  await getRepository(Kpi, tx).delete({ domain: { id: domain.id }, id })
159
277
  await deleteAttachmentsByRef(null, { refBys: [id] }, context)
160
278
 
@@ -163,10 +281,7 @@ export class KpiMutation {
163
281
 
164
282
  @Directive('@transaction')
165
283
  @Mutation(returns => Boolean, { description: 'To delete multiple Kpis' })
166
- async deleteKpis(
167
- @Arg('ids', type => [String]) ids: string[],
168
- @Ctx() context: ResolverContext
169
- ): Promise<boolean> {
284
+ async deleteKpis(@Arg('ids', type => [String]) ids: string[], @Ctx() context: ResolverContext): Promise<boolean> {
170
285
  const { domain, tx } = context.state
171
286
 
172
287
  await getRepository(Kpi, tx).delete({
@@ -195,4 +310,71 @@ export class KpiMutation {
195
310
 
196
311
  return true
197
312
  }
313
+
314
+ @Directive('@transaction')
315
+ @Mutation(returns => Kpi, { description: 'Release a KPI and create a version history.' })
316
+ async releaseKpi(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Kpi> {
317
+ const { domain, user, tx } = context.state
318
+ const repository = getRepository(Kpi, tx)
319
+ const historyRepository = getRepository(KpiHistory, tx)
320
+
321
+ const kpi = await repository.findOne({
322
+ where: { domain: { id: domain.id }, id }
323
+ })
324
+ if (!kpi) throw `KPI given id(${id}) is not found`
325
+ if (kpi.state == 'RELEASE') throw `KPI given id(${id}) is already released`
326
+
327
+ // 히스토리에서 max version 가져오기
328
+ const maxHistory = await historyRepository
329
+ .createQueryBuilder('history')
330
+ .select('MAX(history.version)', 'max')
331
+ .where('history.originalId = :id', { id: kpi.id })
332
+ .getRawOne()
333
+ const nextVersion = maxHistory?.max ? Number(maxHistory.max) + 1 : 1
334
+
335
+ const updated = await repository.save({
336
+ ...kpi,
337
+ version: nextVersion,
338
+ state: 'RELEASE',
339
+ updater: user
340
+ } as any)
341
+ return updated
342
+ }
343
+
344
+ @Directive('@transaction')
345
+ @Mutation(returns => Kpi, { description: 'Revert a KPI to a specific historical version.' })
346
+ async revertKpiVersion(
347
+ @Arg('id') id: string,
348
+ @Arg('version') version: number,
349
+ @Ctx() context: ResolverContext
350
+ ): Promise<Kpi> {
351
+ const { domain, user, tx } = context.state
352
+ const repository = getRepository(Kpi, tx)
353
+ const historyRepository = getRepository(KpiHistory, tx)
354
+
355
+ const kpi = await repository.findOne({
356
+ where: { domain: { id: domain.id }, id }
357
+ })
358
+ if (!kpi) throw `KPI with id(${id}) is not found`
359
+
360
+ const kpiHistory = await historyRepository.findOne({
361
+ where: { domain: { id: domain.id }, originalId: id, version },
362
+ order: { version: 'DESC' }
363
+ })
364
+ if (!kpiHistory) throw `KPI with id:version(${id}:${version}) is not found`
365
+
366
+ const updated = await repository.save({
367
+ ...kpi,
368
+ name: kpiHistory.name,
369
+ description: kpiHistory.description,
370
+ category: kpiHistory.category,
371
+ categoryId: kpiHistory.categoryId,
372
+ formula: kpiHistory.formula,
373
+ active: kpiHistory.active,
374
+ state: 'DRAFT',
375
+ thumbnail: kpiHistory.thumbnail,
376
+ updater: user
377
+ } as any)
378
+ return updated
379
+ }
198
380
  }
@@ -4,11 +4,19 @@ import { Domain, getQueryBuilderFromListParams, getRepository, ListParam } from
4
4
  import { User } from '@things-factory/auth-base'
5
5
  import { Kpi } from './kpi'
6
6
  import { KpiList } from './kpi-type'
7
+ import { KpiGrade } from '../kpi-grade/kpi-grade'
8
+ import { KpiValue } from '../kpi-value/kpi-value'
9
+ import { KpiHistory } from './kpi-history'
10
+ import { Int } from 'type-graphql'
11
+ import { KpiCategory } from '../kpi-category/kpi-category'
7
12
 
8
13
  @Resolver(Kpi)
9
14
  export class KpiQuery {
10
- @Query(returns => Kpi!, { nullable: true, description: 'To fetch a Kpi' })
11
- async kpi(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Kpi> {
15
+ @Query(returns => Kpi!, { nullable: true, description: 'Fetch a single KPI by its unique identifier.' })
16
+ async kpi(
17
+ @Arg('id', { description: 'Unique identifier of the KPI to fetch.' }) id: string,
18
+ @Ctx() context: ResolverContext
19
+ ): Promise<Kpi> {
12
20
  const { domain } = context.state
13
21
 
14
22
  return await getRepository(Kpi).findOne({
@@ -45,6 +53,11 @@ export class KpiQuery {
45
53
  return attachment?.fullpath
46
54
  }
47
55
 
56
+ @FieldResolver(type => [KpiGrade])
57
+ async grades(@Root() kpi: Kpi): Promise<KpiGrade[]> {
58
+ return await getRepository(KpiGrade).find({ where: { domain: { id: kpi.domainId }, kpi: { id: kpi.id } } })
59
+ }
60
+
48
61
  @FieldResolver(type => Domain)
49
62
  async domain(@Root() kpi: Kpi): Promise<Domain> {
50
63
  return kpi.domainId && (await getRepository(Domain).findOneBy({ id: kpi.domainId }))
@@ -59,4 +72,46 @@ export class KpiQuery {
59
72
  async creator(@Root() kpi: Kpi): Promise<User> {
60
73
  return kpi.creatorId && (await getRepository(User).findOneBy({ id: kpi.creatorId }))
61
74
  }
75
+
76
+ @FieldResolver(type => KpiValue, { nullable: true })
77
+ async value(@Root() kpi: Kpi, @Ctx() context): Promise<KpiValue | null> {
78
+ const { domain } = context.state
79
+ return await getRepository(KpiValue).findOne({
80
+ where: { domain: { id: domain.id }, kpi: { id: kpi.id } },
81
+ order: { valueDate: 'DESC' }
82
+ })
83
+ }
84
+
85
+ @FieldResolver(type => Number, { nullable: true })
86
+ targetValue(@Root() kpi: Kpi): number | undefined {
87
+ return kpi.vizMeta?.targetValue
88
+ }
89
+
90
+ @FieldResolver(type => String, { nullable: true })
91
+ unit(@Root() kpi: Kpi): string | undefined {
92
+ return kpi.vizMeta?.unit
93
+ }
94
+
95
+ @FieldResolver(type => KpiCategory, { nullable: true })
96
+ async category(@Root() kpi: Kpi): Promise<KpiCategory | null> {
97
+ if (!kpi.categoryId) return null
98
+ return await getRepository(KpiCategory).findOneBy({ id: kpi.categoryId })
99
+ }
100
+
101
+ @FieldResolver(type => [KpiHistory], { nullable: true })
102
+ async histories(
103
+ @Root() kpi: Kpi,
104
+ @Ctx() context,
105
+ @Arg('limit', type => Int, { nullable: true }) limit?: number
106
+ ): Promise<KpiHistory[]> {
107
+ const { domain } = context.state
108
+ const qb = getRepository(KpiHistory)
109
+ .createQueryBuilder('history')
110
+ .where('history.domain = :domainId', { domainId: domain.id })
111
+ .andWhere('history.kpi = :kpiId', { kpiId: kpi.id })
112
+ .orderBy('history.updatedAt', 'DESC')
113
+ if (limit) qb.limit(limit)
114
+ else qb.limit(1)
115
+ return await qb.getMany()
116
+ }
62
117
  }
@@ -6,49 +6,102 @@ import { ObjectRef, ScalarObject } from '@things-factory/shell'
6
6
 
7
7
  import { Kpi, KpiStatus } from './kpi'
8
8
 
9
- @InputType()
9
+ @InputType({ description: 'Input type for creating a new KPI. Used in mutations to provide KPI details.' })
10
10
  export class NewKpi {
11
- @Field()
11
+ @Field({ description: 'Name of the KPI.' })
12
12
  name: string
13
13
 
14
- @Field({ nullable: true })
14
+ @Field({ nullable: true, description: 'Detailed description of the KPI.' })
15
15
  description?: string
16
16
 
17
- @Field(type => KpiStatus, { nullable: true })
18
- state?: KpiStatus
17
+ @Field(type => ID, { nullable: true, description: 'ID of the category to which this KPI belongs.' })
18
+ categoryId?: string
19
+
20
+ @Field({ nullable: true, description: 'Calculation formula for the KPI, using metric codes and operators.' })
21
+ formula?: string
19
22
 
20
- @Field({ nullable: true })
23
+ @Field({ nullable: true, description: 'Indicates whether this KPI is active and usable.' })
21
24
  active?: boolean
22
25
 
23
- @Field({ nullable: true })
24
- params?: string
26
+ @Field(type => KpiStatus, { nullable: true, description: 'Current state of the KPI (DRAFT, RELEASED, ARCHIVED).' })
27
+ state?: KpiStatus
25
28
 
26
- @Field(type => GraphQLUpload, { nullable: true })
29
+ @Field(type => GraphQLUpload, { nullable: true, description: 'Thumbnail image or file for this KPI.' })
27
30
  thumbnail?: FileUpload
31
+
32
+ @Field({
33
+ nullable: true,
34
+ description: 'Visualization type for this KPI (e.g., bar, line, gauge, progress, card, table, icon, badge, text).'
35
+ })
36
+ vizType?: string
37
+
38
+ @Field(type => ScalarObject, {
39
+ nullable: true,
40
+ description:
41
+ 'Visualization options and metadata for this KPI, such as color, icon, thresholds, unit, decimal places, etc.'
42
+ })
43
+ vizMeta?: any
44
+
45
+ @Field({
46
+ nullable: true,
47
+ description: 'Cron schedule string for periodic KPI value aggregation (e.g., "0 0 * * *" for daily).'
48
+ })
49
+ schedule?: string
50
+
51
+ @Field({ nullable: true, description: 'Timezone for the KPI schedule.' })
52
+ timezone?: string
28
53
  }
29
54
 
30
- @InputType()
55
+ @InputType({ description: 'Input type for updating an existing KPI. Used in mutations to patch KPI details.' })
31
56
  export class KpiPatch {
32
- @Field(type => ID, { nullable: true })
57
+ @Field(type => ID, { nullable: true, description: 'ID of the KPI to update.' })
33
58
  id?: string
34
59
 
35
- @Field({ nullable: true })
60
+ @Field({ nullable: true, description: 'Name of the KPI.' })
36
61
  name?: string
37
62
 
38
- @Field({ nullable: true })
63
+ @Field({ nullable: true, description: 'Detailed description of the KPI.' })
39
64
  description?: string
40
65
 
41
- @Field(type => KpiStatus, { nullable: true })
42
- state?: KpiStatus
66
+ @Field(type => ID, { nullable: true, description: 'ID of the category to which this KPI belongs.' })
67
+ categoryId?: string
68
+
69
+ @Field({ nullable: true, description: 'Calculation formula for the KPI, using metric codes and operators.' })
70
+ formula?: string
43
71
 
44
- @Field({ nullable: true })
72
+ @Field({ nullable: true, description: 'Indicates whether this KPI is active and usable.' })
45
73
  active?: boolean
46
74
 
47
- @Field(type => GraphQLUpload, { nullable: true })
75
+ @Field(type => KpiStatus, { nullable: true, description: 'Current state of the KPI (DRAFT, RELEASED, ARCHIVED).' })
76
+ state?: KpiStatus
77
+
78
+ @Field(type => GraphQLUpload, { nullable: true, description: 'Thumbnail image or file for this KPI.' })
48
79
  thumbnail?: FileUpload
49
-
50
- @Field({ nullable: true })
80
+
81
+ @Field({
82
+ nullable: true,
83
+ description: 'Visualization type for this KPI (e.g., bar, line, gauge, progress, card, table, icon, badge, text).'
84
+ })
85
+ vizType?: string
86
+
87
+ @Field(type => ScalarObject, {
88
+ nullable: true,
89
+ description:
90
+ 'Visualization options and metadata for this KPI, such as color, icon, thresholds, unit, decimal places, etc.'
91
+ })
92
+ vizMeta?: any
93
+
94
+ @Field({ nullable: true, description: 'Custom flag for update operations (internal use).' })
51
95
  cuFlag?: string
96
+
97
+ @Field({
98
+ nullable: true,
99
+ description: 'Cron schedule string for periodic KPI value aggregation (e.g., "0 0 * * *" for daily).'
100
+ })
101
+ schedule?: string
102
+
103
+ @Field({ nullable: true, description: 'Timezone for the KPI schedule.' })
104
+ timezone?: string
52
105
  }
53
106
 
54
107
  @ObjectType()
@@ -7,6 +7,7 @@ import {
7
7
  Column,
8
8
  RelationId,
9
9
  ManyToOne,
10
+ OneToMany,
10
11
  VersionColumn,
11
12
  PrimaryGeneratedColumn
12
13
  } from 'typeorm'
@@ -14,15 +15,23 @@ import { ObjectType, Field, Int, ID, registerEnumType } from 'type-graphql'
14
15
 
15
16
  import { Domain } from '@things-factory/shell'
16
17
  import { User } from '@things-factory/auth-base'
18
+ import { KpiCategory } from '../kpi-category/kpi-category'
19
+ import { KpiGrade } from '../kpi-grade/kpi-grade'
20
+ import { config } from '@things-factory/env'
21
+ import { ScalarObject } from '@things-factory/shell'
22
+
23
+ const ORMCONFIG = config.get('ormconfig', {})
24
+ const DATABASE_TYPE = ORMCONFIG.type
17
25
 
18
26
  export enum KpiStatus {
19
- STATUS_A = 'STATUS_A',
20
- STATUS_B = 'STATUS_B'
27
+ DRAFT = 'DRAFT',
28
+ RELEASE = 'RELEASE',
29
+ ARCHIVED = 'ARCHIVED'
21
30
  }
22
31
 
23
32
  registerEnumType(KpiStatus, {
24
33
  name: 'KpiStatus',
25
- description: 'state enumeration of a kpi'
34
+ description: 'State enumeration of a KPI (DRAFT, RELEASED, ARCHIVED)'
26
35
  })
27
36
 
28
37
  @Entity()
@@ -30,69 +39,136 @@ registerEnumType(KpiStatus, {
30
39
  where: '"deleted_at" IS NULL',
31
40
  unique: true
32
41
  })
33
- @ObjectType({ description: 'Entity for Kpi' })
42
+ @ObjectType({
43
+ description:
44
+ 'KPI entity. Represents a key performance indicator with calculation formula, target, category, and other attributes.'
45
+ })
34
46
  export class Kpi {
35
47
  @PrimaryGeneratedColumn('uuid')
36
- @Field(type => ID)
48
+ @Field(type => ID, { description: 'Unique identifier for this KPI.' })
37
49
  readonly id: string
38
-
50
+
39
51
  @VersionColumn()
40
- @Field({ nullable: true })
52
+ @Field({
53
+ nullable: true,
54
+ description:
55
+ 'Version number of the KPI. Increments on each modification. When the KPI is released, a snapshot is saved in kpi-history and the status becomes RELEASED. Editing after release increases the version and sets status to DRAFT.'
56
+ })
41
57
  version?: number = 1
42
58
 
43
59
  @ManyToOne(type => Domain)
44
- @Field({ nullable: true })
60
+ @Field({ nullable: true, description: 'Domain (tenant) to which this KPI belongs.' })
45
61
  domain?: Domain
46
62
 
47
63
  @RelationId((kpi: Kpi) => kpi.domain)
64
+ @Field({ nullable: true, description: 'ID of the domain (tenant) for this KPI.' })
48
65
  domainId?: string
49
66
 
50
67
  @Column()
51
- @Field({ nullable: true })
68
+ @Field({ nullable: true, description: 'Name of the KPI.' })
52
69
  name?: string
53
70
 
54
71
  @Column({ nullable: true })
55
- @Field({ nullable: true })
72
+ @Field({ nullable: true, description: 'Detailed description of the KPI.' })
56
73
  description?: string
57
74
 
75
+ @ManyToOne(() => KpiCategory, { nullable: true })
76
+ @Field(type => KpiCategory, { nullable: true, description: 'Category to which this KPI belongs.' })
77
+ category?: KpiCategory
78
+
79
+ @RelationId((kpi: Kpi) => kpi.category)
80
+ @Field({ nullable: true, description: 'ID of the category for this KPI.' })
81
+ categoryId?: string
82
+
83
+ @Column({
84
+ nullable: true,
85
+ type:
86
+ DATABASE_TYPE == 'mysql' || DATABASE_TYPE == 'mariadb'
87
+ ? 'longtext'
88
+ : DATABASE_TYPE == 'oracle'
89
+ ? 'clob'
90
+ : DATABASE_TYPE == 'mssql'
91
+ ? 'nvarchar'
92
+ : 'varchar',
93
+ length: DATABASE_TYPE == 'mssql' ? 'MAX' : undefined
94
+ })
95
+ @Field({
96
+ nullable: true,
97
+ description:
98
+ 'Calculation formula for the KPI. Expressed as a string using metric codes and operators, e.g., "defect_count / total_count * 100".'
99
+ })
100
+ formula?: string
101
+
58
102
  @Column({ nullable: false, default: false })
59
- @Field({ nullable: true })
103
+ @Field({ nullable: true, description: 'Indicates whether this KPI is active and usable.' })
60
104
  active?: boolean
61
105
 
62
106
  @Column({ nullable: true })
63
- @Field({ nullable: true })
107
+ @Field({ nullable: true, description: 'Current state of the KPI (DRAFT, RELEASED, ARCHIVED).' })
64
108
  state?: KpiStatus
65
109
 
66
110
  @Column({ nullable: true })
67
- @Field({ nullable: true })
68
- params?: string
111
+ @Field({
112
+ nullable: true,
113
+ description: 'Visualization type for this KPI (e.g., bar, line, gauge, progress, card, table, icon, badge, text).'
114
+ })
115
+ vizType?: string
116
+
117
+ @Column({ type: 'json', nullable: true })
118
+ @Field(type => ScalarObject, {
119
+ nullable: true,
120
+ description:
121
+ 'Visualization options and metadata for this KPI, such as color, icon, thresholds, unit, decimal places, etc.'
122
+ })
123
+ vizMeta?: any
124
+
125
+ @Column({ nullable: true })
126
+ @Field({
127
+ nullable: true,
128
+ description: 'Cron schedule string for periodic KPI value aggregation (e.g., "0 0 * * *" for daily).'
129
+ })
130
+ schedule?: string
131
+
132
+ @Column({ nullable: true })
133
+ @Field({ nullable: true, description: 'Schedule ID for the KPI (used for scheduler registration).' })
134
+ scheduleId?: string
135
+
136
+ @Column({ nullable: true })
137
+ @Field({ nullable: true, description: 'Timezone for the KPI schedule.' })
138
+ timezone?: string
69
139
 
70
140
  @CreateDateColumn()
71
- @Field({ nullable: true })
141
+ @Field({ nullable: true, description: 'Timestamp when this KPI was created.' })
72
142
  createdAt?: Date
73
143
 
74
144
  @UpdateDateColumn()
75
- @Field({ nullable: true })
145
+ @Field({ nullable: true, description: 'Timestamp when this KPI was last updated.' })
76
146
  updatedAt?: Date
77
147
 
78
148
  @DeleteDateColumn()
79
- @Field({ nullable: true })
149
+ @Field({ nullable: true, description: 'Timestamp when this KPI was deleted (soft delete).' })
80
150
  deletedAt?: Date
81
151
 
82
152
  @ManyToOne(type => User, { nullable: true })
83
- @Field(type => User, { nullable: true })
153
+ @Field(type => User, { nullable: true, description: 'User who created this KPI.' })
84
154
  creator?: User
85
155
 
86
156
  @RelationId((kpi: Kpi) => kpi.creator)
157
+ @Field({ nullable: true, description: 'ID of the user who created this KPI.' })
87
158
  creatorId?: string
88
159
 
89
160
  @ManyToOne(type => User, { nullable: true })
90
- @Field(type => User, { nullable: true })
161
+ @Field(type => User, { nullable: true, description: 'User who last updated this KPI.' })
91
162
  updater?: User
92
163
 
93
164
  @RelationId((kpi: Kpi) => kpi.updater)
165
+ @Field({ nullable: true, description: 'ID of the user who last updated this KPI.' })
94
166
  updaterId?: string
95
167
 
96
- @Field(type => String, { nullable: true })
168
+ @Field(type => String, { nullable: true, description: 'Thumbnail image or file path for this KPI.' })
97
169
  thumbnail?: string
170
+
171
+ @OneToMany(type => KpiGrade, kpiGrade => kpiGrade.kpi)
172
+ @Field(type => [KpiGrade], { nullable: true, description: 'List of grades for this KPI.' })
173
+ grades?: KpiGrade[]
98
174
  }
@@ -0,0 +1,3 @@
1
+ import { KpiAlertQuery } from './kpi-alert-query'
2
+
3
+ export const resolvers = [KpiAlertQuery]