@things-factory/kpi 9.0.30 → 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 (347) hide show
  1. package/README.md +1 -2
  2. package/client/charts/kpi-boxplot-chart.ts +182 -42
  3. package/client/charts/kpi-mini-trend-chart.ts +125 -0
  4. package/client/charts/kpi-radar-chart.ts +9 -9
  5. package/client/charts/kpi-trend-chart.ts +163 -0
  6. package/client/google-map/common-google-map.ts +45 -7
  7. package/client/google-map/google-map-loader.ts +1 -1
  8. package/client/pages/kpi/kpi-list-page.ts +196 -32
  9. package/client/pages/kpi/kpi-overview.ts +9 -11
  10. package/client/pages/kpi/kpi-tree-page.ts +409 -0
  11. package/client/pages/kpi/kpi-view.ts +187 -0
  12. package/client/pages/kpi-dashboard/cards/kpi-level1-card.ts +1 -1
  13. package/client/pages/kpi-dashboard/cards/kpi-level2-comparison.ts +1 -1
  14. package/client/pages/kpi-dashboard/cards/kpi-level3-comparison.ts +1 -1
  15. package/client/pages/kpi-dashboard/components/kpi-chart-toggle.ts +1 -2
  16. package/client/pages/kpi-dashboard/components/kpi-left-panel.ts +437 -0
  17. package/client/pages/kpi-dashboard/components/kpi-map-panel.ts +243 -30
  18. package/client/pages/kpi-dashboard/components/kpi-region-popup.ts +356 -0
  19. package/client/pages/kpi-dashboard/kpi-dashboard-map.ts +50 -592
  20. package/client/pages/kpi-dashboard/kpi-dashboard.ts +28 -30
  21. package/client/pages/kpi-history/kpi-history-list-page.ts +11 -11
  22. package/client/pages/kpi-metric/kpi-metric-list-page.ts +10 -2
  23. package/client/pages/kpi-metric-value/kpi-metric-value-editor-page.ts +7 -7
  24. package/client/pages/kpi-metric-value/kpi-metric-value-importer.ts +2 -2
  25. package/client/pages/kpi-metric-value/kpi-metric-value-list-page.ts +16 -8
  26. package/client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.ts +5 -5
  27. package/client/pages/kpi-statistic/kpi-statistic-editor-page.ts +1 -2
  28. package/client/pages/kpi-statistic/kpi-statistic-list-page.ts +10 -2
  29. package/client/pages/kpi-value/kpi-value-editor-page.ts +11 -7
  30. package/client/pages/kpi-value/kpi-value-list-page.ts +31 -7
  31. package/client/route.ts +2 -9
  32. package/design-entities.md +8 -12
  33. package/dist-client/charts/kpi-boxplot-chart.d.ts +2 -0
  34. package/dist-client/charts/kpi-boxplot-chart.js +168 -42
  35. package/dist-client/charts/kpi-boxplot-chart.js.map +1 -1
  36. package/dist-client/charts/kpi-mini-trend-chart.d.ts +14 -0
  37. package/dist-client/charts/kpi-mini-trend-chart.js +148 -0
  38. package/dist-client/charts/kpi-mini-trend-chart.js.map +1 -0
  39. package/dist-client/charts/kpi-radar-chart.js +9 -9
  40. package/dist-client/charts/kpi-radar-chart.js.map +1 -1
  41. package/dist-client/charts/kpi-trend-chart.d.ts +25 -0
  42. package/dist-client/charts/kpi-trend-chart.js +186 -0
  43. package/dist-client/charts/kpi-trend-chart.js.map +1 -0
  44. package/dist-client/google-map/common-google-map.js +40 -7
  45. package/dist-client/google-map/common-google-map.js.map +1 -1
  46. package/dist-client/google-map/google-map-loader.js +1 -1
  47. package/dist-client/google-map/google-map-loader.js.map +1 -1
  48. package/dist-client/pages/kpi/kpi-list-page.d.ts +19 -3
  49. package/dist-client/pages/kpi/kpi-list-page.js +188 -32
  50. package/dist-client/pages/kpi/kpi-list-page.js.map +1 -1
  51. package/dist-client/pages/kpi/kpi-overview.js +9 -11
  52. package/dist-client/pages/kpi/kpi-overview.js.map +1 -1
  53. package/dist-client/pages/kpi/kpi-tree-page.d.ts +59 -0
  54. package/dist-client/pages/kpi/kpi-tree-page.js +403 -0
  55. package/dist-client/pages/kpi/kpi-tree-page.js.map +1 -0
  56. package/dist-client/pages/kpi/kpi-view.d.ts +12 -0
  57. package/dist-client/pages/kpi/kpi-view.js +191 -0
  58. package/dist-client/pages/kpi/kpi-view.js.map +1 -0
  59. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js +1 -1
  60. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js.map +1 -1
  61. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js +1 -1
  62. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js.map +1 -1
  63. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js +1 -1
  64. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js.map +1 -1
  65. package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.js +1 -2
  66. package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.js.map +1 -1
  67. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.d.ts +24 -0
  68. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js +440 -0
  69. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js.map +1 -0
  70. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.d.ts +11 -1
  71. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js +243 -22
  72. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js.map +1 -1
  73. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.d.ts +23 -0
  74. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js +369 -0
  75. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js.map +1 -0
  76. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.d.ts +6 -15
  77. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js +47 -588
  78. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js.map +1 -1
  79. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js +28 -30
  80. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js.map +1 -1
  81. package/dist-client/pages/kpi-history/kpi-history-list-page.d.ts +6 -1
  82. package/dist-client/pages/kpi-history/kpi-history-list-page.js +11 -11
  83. package/dist-client/pages/kpi-history/kpi-history-list-page.js.map +1 -1
  84. package/dist-client/pages/kpi-metric/kpi-metric-list-page.d.ts +5 -0
  85. package/dist-client/pages/kpi-metric/kpi-metric-list-page.js +10 -2
  86. package/dist-client/pages/kpi-metric/kpi-metric-list-page.js.map +1 -1
  87. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.d.ts +1 -1
  88. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js +8 -8
  89. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js.map +1 -1
  90. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js +2 -2
  91. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js.map +1 -1
  92. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.d.ts +5 -0
  93. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js +16 -8
  94. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js.map +1 -1
  95. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.d.ts +1 -1
  96. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js +6 -6
  97. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js.map +1 -1
  98. package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.js +1 -2
  99. package/dist-client/pages/kpi-statistic/kpi-statistic-editor-page.js.map +1 -1
  100. package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.d.ts +5 -0
  101. package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.js +10 -2
  102. package/dist-client/pages/kpi-statistic/kpi-statistic-list-page.js.map +1 -1
  103. package/dist-client/pages/kpi-value/kpi-value-editor-page.d.ts +2 -1
  104. package/dist-client/pages/kpi-value/kpi-value-editor-page.js +16 -8
  105. package/dist-client/pages/kpi-value/kpi-value-editor-page.js.map +1 -1
  106. package/dist-client/pages/kpi-value/kpi-value-list-page.d.ts +5 -0
  107. package/dist-client/pages/kpi-value/kpi-value-list-page.js +31 -7
  108. package/dist-client/pages/kpi-value/kpi-value-list-page.js.map +1 -1
  109. package/dist-client/route.d.ts +1 -1
  110. package/dist-client/route.js +2 -8
  111. package/dist-client/route.js.map +1 -1
  112. package/dist-client/tsconfig.tsbuildinfo +1 -1
  113. package/dist-server/controllers/kpi-metric-value-provider.d.ts +1 -1
  114. package/dist-server/controllers/kpi-metric-value-provider.js +4 -4
  115. package/dist-server/controllers/kpi-metric-value-provider.js.map +1 -1
  116. package/dist-server/controllers/kpi-value-provider.d.ts +1 -1
  117. package/dist-server/controllers/kpi-value-provider.js +3 -3
  118. package/dist-server/controllers/kpi-value-provider.js.map +1 -1
  119. package/dist-server/migrations/1752190849680-seed-kpi-metrics.d.ts +6 -0
  120. package/dist-server/migrations/1752190849680-seed-kpi-metrics.js +101 -0
  121. package/dist-server/migrations/1752190849680-seed-kpi-metrics.js.map +1 -0
  122. package/dist-server/migrations/1752190849681-seed-kpi.d.ts +5 -0
  123. package/dist-server/migrations/1752190849681-seed-kpi.js +315 -0
  124. package/dist-server/migrations/1752190849681-seed-kpi.js.map +1 -0
  125. package/dist-server/migrations/1752192090123-add-grades-to-kpi.d.ts +7 -0
  126. package/dist-server/migrations/1752192090123-add-grades-to-kpi.js +51 -0
  127. package/dist-server/migrations/1752192090123-add-grades-to-kpi.js.map +1 -0
  128. package/dist-server/migrations/1752192090124-add-kpi-statistics.d.ts +5 -0
  129. package/dist-server/migrations/1752192090124-add-kpi-statistics.js +710 -0
  130. package/dist-server/migrations/1752192090124-add-kpi-statistics.js.map +1 -0
  131. package/dist-server/migrations/1752192090128-seed-kpi-org-scope.d.ts +6 -0
  132. package/dist-server/migrations/1752192090128-seed-kpi-org-scope.js +111 -0
  133. package/dist-server/migrations/1752192090128-seed-kpi-org-scope.js.map +1 -0
  134. package/dist-server/migrations/1752192090129-seed-kpi-values.d.ts +6 -0
  135. package/dist-server/migrations/1752192090129-seed-kpi-values.js +187 -0
  136. package/dist-server/migrations/1752192090129-seed-kpi-values.js.map +1 -0
  137. package/dist-server/migrations/grade-data/x11-performance-table.json +962 -0
  138. package/dist-server/migrations/grade-data/x12-performance-table.json +611 -0
  139. package/dist-server/migrations/grade-data/x14-performance-table.json +42 -0
  140. package/dist-server/migrations/grade-data/x21-performance-table.json +889 -0
  141. package/dist-server/migrations/grade-data/x22-performance-table.json +1064 -0
  142. package/dist-server/migrations/grade-data/x23-performance-table.json +42 -0
  143. package/dist-server/migrations/grade-data/x31-performance-table.json +644 -0
  144. package/dist-server/migrations/grade-data/x32-performance-table.json +993 -0
  145. package/dist-server/migrations/grade-data/x33-performance-table.json +195 -0
  146. package/dist-server/migrations/grade-data/x34-performance-table.json +12 -0
  147. package/dist-server/migrations/grade-data/x35-performance-table.json +42 -0
  148. package/dist-server/migrations/grade-data/x41-performance-table.json +825 -0
  149. package/dist-server/migrations/grade-data/x42-performance-table.json +786 -0
  150. package/dist-server/migrations/grade-data/x43-performance-table.json +12 -0
  151. package/dist-server/migrations/grade-data/x44-performance-table.json +42 -0
  152. package/dist-server/migrations/grade-data/x51-performance-table.json +924 -0
  153. package/dist-server/migrations/grade-data/x52-performance-table.json +42 -0
  154. package/dist-server/migrations/grade-data/x61-performance-table.json +261 -0
  155. package/dist-server/migrations/grade-data/x62-performance-table.json +42 -0
  156. package/dist-server/migrations/seed-data/kpi-metrics-seed.json +454 -0
  157. package/dist-server/migrations/seed-data/kpi-org-scope-seed.json +1676 -0
  158. package/dist-server/migrations/seed-data/kpi-values-seed.json +402 -0
  159. package/dist-server/migrations/seed-data/kpis-seed.json +488 -0
  160. package/dist-server/service/index.d.ts +3 -7
  161. package/dist-server/service/index.js +5 -12
  162. package/dist-server/service/index.js.map +1 -1
  163. package/dist-server/service/kpi/aggregate-kpi.js +30 -13
  164. package/dist-server/service/kpi/aggregate-kpi.js.map +1 -1
  165. package/dist-server/service/kpi/kpi-formula.service.d.ts +15 -0
  166. package/dist-server/service/kpi/kpi-formula.service.js +90 -0
  167. package/dist-server/service/kpi/kpi-formula.service.js.map +1 -1
  168. package/dist-server/service/kpi/kpi-history.d.ts +0 -3
  169. package/dist-server/service/kpi/kpi-history.js +0 -10
  170. package/dist-server/service/kpi/kpi-history.js.map +1 -1
  171. package/dist-server/service/kpi/kpi-mutation.d.ts +1 -1
  172. package/dist-server/service/kpi/kpi-mutation.js +57 -20
  173. package/dist-server/service/kpi/kpi-mutation.js.map +1 -1
  174. package/dist-server/service/kpi/kpi-query.d.ts +7 -3
  175. package/dist-server/service/kpi/kpi-query.js +126 -10
  176. package/dist-server/service/kpi/kpi-query.js.map +1 -1
  177. package/dist-server/service/kpi/kpi-type.d.ts +4 -2
  178. package/dist-server/service/kpi/kpi-type.js +12 -4
  179. package/dist-server/service/kpi/kpi-type.js.map +1 -1
  180. package/dist-server/service/kpi/kpi.d.ts +4 -3
  181. package/dist-server/service/kpi/kpi.js +20 -8
  182. package/dist-server/service/kpi/kpi.js.map +1 -1
  183. package/dist-server/service/kpi-metric/aggregate-kpi-metric.js +46 -11
  184. package/dist-server/service/kpi-metric/aggregate-kpi-metric.js.map +1 -1
  185. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.d.ts +1 -1
  186. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js +6 -6
  187. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js.map +1 -1
  188. package/dist-server/service/kpi-metric-value/kpi-metric-value-type.d.ts +2 -2
  189. package/dist-server/service/kpi-metric-value/kpi-metric-value-type.js +4 -4
  190. package/dist-server/service/kpi-metric-value/kpi-metric-value-type.js.map +1 -1
  191. package/dist-server/service/kpi-metric-value/kpi-metric-value.d.ts +1 -1
  192. package/dist-server/service/kpi-metric-value/kpi-metric-value.js +3 -3
  193. package/dist-server/service/kpi-metric-value/kpi-metric-value.js.map +1 -1
  194. package/dist-server/service/kpi-org-scope/index.d.ts +9 -0
  195. package/dist-server/service/kpi-org-scope/index.js +14 -0
  196. package/dist-server/service/kpi-org-scope/index.js.map +1 -0
  197. package/dist-server/service/kpi-org-scope/kpi-org-scope-mutation.d.ts +8 -0
  198. package/dist-server/service/kpi-org-scope/kpi-org-scope-mutation.js +170 -0
  199. package/dist-server/service/kpi-org-scope/kpi-org-scope-mutation.js.map +1 -0
  200. package/dist-server/service/kpi-org-scope/kpi-org-scope-query.d.ts +14 -0
  201. package/dist-server/service/kpi-org-scope/kpi-org-scope-query.js +152 -0
  202. package/dist-server/service/kpi-org-scope/kpi-org-scope-query.js.map +1 -0
  203. package/dist-server/service/kpi-org-scope/kpi-org-scope-type.d.ts +26 -0
  204. package/dist-server/service/kpi-org-scope/kpi-org-scope-type.js +101 -0
  205. package/dist-server/service/kpi-org-scope/kpi-org-scope-type.js.map +1 -0
  206. package/dist-server/service/kpi-org-scope/kpi-org-scope.d.ts +26 -0
  207. package/dist-server/service/kpi-org-scope/kpi-org-scope.js +135 -0
  208. package/dist-server/service/kpi-org-scope/kpi-org-scope.js.map +1 -0
  209. package/dist-server/service/kpi-statistic/kpi-statistic.d.ts +1 -0
  210. package/dist-server/service/kpi-statistic/kpi-statistic.js +11 -0
  211. package/dist-server/service/kpi-statistic/kpi-statistic.js.map +1 -1
  212. package/dist-server/service/kpi-value/kpi-value-mutation.js +71 -7
  213. package/dist-server/service/kpi-value/kpi-value-mutation.js.map +1 -1
  214. package/dist-server/service/kpi-value/kpi-value-type.d.ts +4 -2
  215. package/dist-server/service/kpi-value/kpi-value-type.js +12 -4
  216. package/dist-server/service/kpi-value/kpi-value-type.js.map +1 -1
  217. package/dist-server/service/kpi-value/kpi-value.d.ts +3 -1
  218. package/dist-server/service/kpi-value/kpi-value.js +11 -5
  219. package/dist-server/service/kpi-value/kpi-value.js.map +1 -1
  220. package/dist-server/service/utils/value-date-util.d.ts +1 -0
  221. package/dist-server/service/utils/value-date-util.js +41 -0
  222. package/dist-server/service/utils/value-date-util.js.map +1 -1
  223. package/dist-server/tsconfig.json +10 -0
  224. package/dist-server/tsconfig.tsbuildinfo +1 -1
  225. package/package.json +7 -6
  226. package/server/@types/index.d.ts +11 -0
  227. package/server/controllers/kpi-metric-value-provider.ts +5 -5
  228. package/server/controllers/kpi-value-provider.ts +4 -4
  229. package/server/migrations/1752190849680-seed-kpi-metrics.ts +124 -0
  230. package/server/migrations/1752190849681-seed-kpi.ts +356 -0
  231. package/server/migrations/1752192090123-add-grades-to-kpi.ts +67 -0
  232. package/server/migrations/1752192090124-add-kpi-statistics.ts +719 -0
  233. package/server/migrations/1752192090128-seed-kpi-org-scope.ts +132 -0
  234. package/server/migrations/1752192090129-seed-kpi-values.ts +207 -0
  235. package/server/migrations/grade-data/x11-performance-table.json +962 -0
  236. package/server/migrations/grade-data/x12-performance-table.json +611 -0
  237. package/server/migrations/grade-data/x14-performance-table.json +42 -0
  238. package/server/migrations/grade-data/x21-performance-table.json +889 -0
  239. package/server/migrations/grade-data/x22-performance-table.json +1064 -0
  240. package/server/migrations/grade-data/x23-performance-table.json +42 -0
  241. package/server/migrations/grade-data/x31-performance-table.json +644 -0
  242. package/server/migrations/grade-data/x32-performance-table.json +993 -0
  243. package/server/migrations/grade-data/x33-performance-table.json +195 -0
  244. package/server/migrations/grade-data/x34-performance-table.json +12 -0
  245. package/server/migrations/grade-data/x35-performance-table.json +42 -0
  246. package/server/migrations/grade-data/x41-performance-table.json +825 -0
  247. package/server/migrations/grade-data/x42-performance-table.json +786 -0
  248. package/server/migrations/grade-data/x43-performance-table.json +12 -0
  249. package/server/migrations/grade-data/x44-performance-table.json +42 -0
  250. package/server/migrations/grade-data/x51-performance-table.json +924 -0
  251. package/server/migrations/grade-data/x52-performance-table.json +42 -0
  252. package/server/migrations/grade-data/x61-performance-table.json +261 -0
  253. package/server/migrations/grade-data/x62-performance-table.json +42 -0
  254. package/server/migrations/seed-data/kpi-metrics-seed.json +454 -0
  255. package/server/migrations/seed-data/kpi-org-scope-seed.json +1676 -0
  256. package/server/migrations/seed-data/kpi-values-seed.json +402 -0
  257. package/server/migrations/seed-data/kpis-seed.json +488 -0
  258. package/server/service/index.ts +5 -12
  259. package/server/service/kpi/aggregate-kpi.ts +31 -13
  260. package/server/service/kpi/kpi-formula.service.ts +101 -0
  261. package/server/service/kpi/kpi-history.ts +0 -8
  262. package/server/service/kpi/kpi-mutation.ts +59 -19
  263. package/server/service/kpi/kpi-query.ts +118 -8
  264. package/server/service/kpi/kpi-type.ts +10 -4
  265. package/server/service/kpi/kpi.ts +17 -7
  266. package/server/service/kpi-metric/aggregate-kpi-metric.ts +55 -11
  267. package/server/service/kpi-metric-value/kpi-metric-value-mutation.ts +6 -6
  268. package/server/service/kpi-metric-value/kpi-metric-value-type.ts +4 -4
  269. package/server/service/kpi-metric-value/kpi-metric-value.ts +3 -3
  270. package/server/service/kpi-org-scope/index.ts +11 -0
  271. package/server/service/kpi-org-scope/kpi-org-scope-mutation.ts +173 -0
  272. package/server/service/kpi-org-scope/kpi-org-scope-query.ts +127 -0
  273. package/server/service/kpi-org-scope/kpi-org-scope-type.ts +68 -0
  274. package/server/service/kpi-org-scope/kpi-org-scope.ts +123 -0
  275. package/server/service/kpi-statistic/kpi-statistic.ts +10 -0
  276. package/server/service/kpi-value/kpi-value-mutation.ts +73 -7
  277. package/server/service/kpi-value/kpi-value-type.ts +10 -4
  278. package/server/service/kpi-value/kpi-value.ts +10 -5
  279. package/server/service/utils/value-date-util.ts +47 -0
  280. package/server/types/global.d.ts +8 -0
  281. package/things-factory.config.js +1 -0
  282. package/translations/en.json +15 -3
  283. package/translations/ja.json +13 -3
  284. package/translations/ko.json +15 -3
  285. package/translations/ms.json +13 -3
  286. package/translations/zh.json +13 -3
  287. package/client/google-map/script-loader.ts +0 -173
  288. package/client/pages/kpi-category/kpi-category-importer.ts +0 -90
  289. package/client/pages/kpi-category/kpi-category-list-page.ts +0 -537
  290. package/client/pages/kpi-category/kpi-category-value-calculator.ts +0 -233
  291. package/client/pages/kpi-category-value/kpi-category-value-list-page.ts +0 -404
  292. package/dist-client/google-map/script-loader.d.ts +0 -3
  293. package/dist-client/google-map/script-loader.js +0 -144
  294. package/dist-client/google-map/script-loader.js.map +0 -1
  295. package/dist-client/pages/kpi-category/kpi-category-importer.d.ts +0 -23
  296. package/dist-client/pages/kpi-category/kpi-category-importer.js +0 -92
  297. package/dist-client/pages/kpi-category/kpi-category-importer.js.map +0 -1
  298. package/dist-client/pages/kpi-category/kpi-category-list-page.d.ts +0 -74
  299. package/dist-client/pages/kpi-category/kpi-category-list-page.js +0 -517
  300. package/dist-client/pages/kpi-category/kpi-category-list-page.js.map +0 -1
  301. package/dist-client/pages/kpi-category/kpi-category-value-calculator.d.ts +0 -13
  302. package/dist-client/pages/kpi-category/kpi-category-value-calculator.js +0 -256
  303. package/dist-client/pages/kpi-category/kpi-category-value-calculator.js.map +0 -1
  304. package/dist-client/pages/kpi-category-value/kpi-category-value-list-page.d.ts +0 -63
  305. package/dist-client/pages/kpi-category-value/kpi-category-value-list-page.js +0 -393
  306. package/dist-client/pages/kpi-category-value/kpi-category-value-list-page.js.map +0 -1
  307. package/dist-server/service/kpi-category/index.d.ts +0 -6
  308. package/dist-server/service/kpi-category/index.js +0 -10
  309. package/dist-server/service/kpi-category/index.js.map +0 -1
  310. package/dist-server/service/kpi-category/kpi-category-mutation.d.ts +0 -9
  311. package/dist-server/service/kpi-category/kpi-category-mutation.js +0 -221
  312. package/dist-server/service/kpi-category/kpi-category-mutation.js.map +0 -1
  313. package/dist-server/service/kpi-category/kpi-category-query.d.ts +0 -18
  314. package/dist-server/service/kpi-category/kpi-category-query.js +0 -115
  315. package/dist-server/service/kpi-category/kpi-category-query.js.map +0 -1
  316. package/dist-server/service/kpi-category/kpi-category-type.d.ts +0 -24
  317. package/dist-server/service/kpi-category/kpi-category-type.js +0 -100
  318. package/dist-server/service/kpi-category/kpi-category-type.js.map +0 -1
  319. package/dist-server/service/kpi-category/kpi-category.d.ts +0 -22
  320. package/dist-server/service/kpi-category/kpi-category.js +0 -106
  321. package/dist-server/service/kpi-category/kpi-category.js.map +0 -1
  322. package/dist-server/service/kpi-category-value/index.d.ts +0 -6
  323. package/dist-server/service/kpi-category-value/index.js +0 -10
  324. package/dist-server/service/kpi-category-value/index.js.map +0 -1
  325. package/dist-server/service/kpi-category-value/kpi-category-value-mutation.d.ts +0 -8
  326. package/dist-server/service/kpi-category-value/kpi-category-value-mutation.js +0 -102
  327. package/dist-server/service/kpi-category-value/kpi-category-value-mutation.js.map +0 -1
  328. package/dist-server/service/kpi-category-value/kpi-category-value-query.d.ts +0 -13
  329. package/dist-server/service/kpi-category-value/kpi-category-value-query.js +0 -91
  330. package/dist-server/service/kpi-category-value/kpi-category-value-query.js.map +0 -1
  331. package/dist-server/service/kpi-category-value/kpi-category-value-type.d.ts +0 -19
  332. package/dist-server/service/kpi-category-value/kpi-category-value-type.js +0 -73
  333. package/dist-server/service/kpi-category-value/kpi-category-value-type.js.map +0 -1
  334. package/dist-server/service/kpi-category-value/kpi-category-value.d.ts +0 -19
  335. package/dist-server/service/kpi-category-value/kpi-category-value.js +0 -91
  336. package/dist-server/service/kpi-category-value/kpi-category-value.js.map +0 -1
  337. package/helps/kpi/kpi-category.md +0 -160
  338. package/server/service/kpi-category/index.ts +0 -7
  339. package/server/service/kpi-category/kpi-category-mutation.ts +0 -217
  340. package/server/service/kpi-category/kpi-category-query.ts +0 -87
  341. package/server/service/kpi-category/kpi-category-type.ts +0 -73
  342. package/server/service/kpi-category/kpi-category.ts +0 -95
  343. package/server/service/kpi-category-value/index.ts +0 -7
  344. package/server/service/kpi-category-value/kpi-category-value-mutation.ts +0 -88
  345. package/server/service/kpi-category-value/kpi-category-value-query.ts +0 -62
  346. package/server/service/kpi-category-value/kpi-category-value-type.ts +0 -48
  347. 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
  }
@@ -0,0 +1,125 @@
1
+ import { LitElement, html, css } from 'lit'
2
+ import { customElement, property } from 'lit/decorators.js'
3
+ import * as d3 from 'd3'
4
+
5
+ @customElement('kpi-mini-trend-chart')
6
+ export class KpiMiniTrendChart extends LitElement {
7
+ @property({ type: Array }) data: number[] = []
8
+ @property({ type: Number }) width: number = 60
9
+ @property({ type: Number }) height: number = 30
10
+ @property({ type: String }) lineColor: string = '#2196f3'
11
+ @property({ type: Number }) strokeWidth: number = 1.5
12
+ @property({ type: Boolean }) showPoints: boolean = true
13
+ @property({ type: Number }) pointRadius: number = 1.5
14
+
15
+ static styles = css`
16
+ :host {
17
+ display: block;
18
+ width: 100%;
19
+ height: 100%;
20
+ }
21
+
22
+ .mini-chart {
23
+ width: 100%;
24
+ height: 100%;
25
+ background: #f8f9fa;
26
+ border-radius: 4px;
27
+ display: flex;
28
+ align-items: center;
29
+ justify-content: center;
30
+ }
31
+
32
+ .trend-line {
33
+ fill: none;
34
+ stroke-linecap: round;
35
+ stroke-linejoin: round;
36
+ }
37
+
38
+ .data-point {
39
+ fill: white;
40
+ stroke-width: 1;
41
+ }
42
+ `
43
+
44
+ render() {
45
+ return html`
46
+ <div class="mini-chart">
47
+ <svg
48
+ id="mini-trend"
49
+ width=${this.width}
50
+ height=${this.height}
51
+ viewBox="0 0 ${this.width} ${this.height}"
52
+ preserveAspectRatio="xMidYMid meet"
53
+ ></svg>
54
+ </div>
55
+ `
56
+ }
57
+
58
+ updated() {
59
+ this.drawMiniTrend()
60
+ }
61
+
62
+ drawMiniTrend() {
63
+ if (!this.data || this.data.length === 0) return
64
+
65
+ const svg = d3.select(this.renderRoot.querySelector('#mini-trend'))
66
+ svg.selectAll('*').remove()
67
+
68
+ const margin = { top: 2, right: 2, bottom: 2, left: 2 }
69
+ const width = this.width - margin.left - margin.right
70
+ const height = this.height - margin.top - margin.bottom
71
+
72
+ // 스케일 설정
73
+ const xScale = d3
74
+ .scaleLinear()
75
+ .domain([0, this.data.length - 1])
76
+ .range([0, width])
77
+
78
+ const yScale = d3
79
+ .scaleLinear()
80
+ .domain([0, d3.max(this.data) || 100])
81
+ .range([height, 0])
82
+
83
+ // SVG 설정
84
+ svg.attr('width', this.width).attr('height', this.height)
85
+ const g = svg.append('g').attr('transform', `translate(${margin.left},${margin.top})`)
86
+
87
+ // 라인 생성기
88
+ const line = d3
89
+ .line<number>()
90
+ .x((d, i) => xScale(i))
91
+ .y(d => yScale(d))
92
+ .curve(d3.curveMonotoneX)
93
+
94
+ // 트렌드 라인 그리기
95
+ g.append('path')
96
+ .datum(this.data)
97
+ .attr('class', 'trend-line')
98
+ .attr('d', line as any)
99
+ .attr('stroke', this.lineColor)
100
+ .attr('stroke-width', this.strokeWidth)
101
+
102
+ // 데이터 포인트 그리기 (첫 번째와 마지막 포인트만)
103
+ if (this.showPoints && this.data.length > 0) {
104
+ // 첫 번째 포인트
105
+ g.append('circle')
106
+ .attr('class', 'data-point')
107
+ .attr('cx', xScale(0))
108
+ .attr('cy', yScale(this.data[0]))
109
+ .attr('r', this.pointRadius)
110
+ .attr('stroke', '#4caf50')
111
+ .attr('fill', 'white')
112
+
113
+ // 마지막 포인트
114
+ if (this.data.length > 1) {
115
+ g.append('circle')
116
+ .attr('class', 'data-point')
117
+ .attr('cx', xScale(this.data.length - 1))
118
+ .attr('cy', yScale(this.data[this.data.length - 1]))
119
+ .attr('r', this.pointRadius)
120
+ .attr('stroke', '#2196f3')
121
+ .attr('fill', 'white')
122
+ }
123
+ }
124
+ }
125
+ }
@@ -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
  }
@@ -0,0 +1,163 @@
1
+ import { LitElement, html, css } from 'lit'
2
+ import { customElement, property } from 'lit/decorators.js'
3
+ import * as d3 from 'd3'
4
+
5
+ @customElement('kpi-trend-chart')
6
+ export class KpiTrendChart extends LitElement {
7
+ @property({ type: Array }) data: { date: string; value: number; color?: string }[] = []
8
+ @property({ type: String }) valueKey: string = 'value'
9
+ @property({ type: String }) dateKey: string = 'date'
10
+ @property({ type: Number }) width: number = 400
11
+ @property({ type: Number }) height: number = 200
12
+ @property({ type: String }) lineColor: string = '#2196f3'
13
+ @property({ type: Number }) strokeWidth: number = 2
14
+ @property({ type: Boolean }) showPoints: boolean = true
15
+ @property({ type: Number }) pointRadius: number = 4
16
+
17
+ private chartWidth = 0
18
+ private chartHeight = 0
19
+ private resizeObserver?: ResizeObserver
20
+
21
+ static styles = css`
22
+ :host {
23
+ display: block;
24
+ width: 100%;
25
+ height: 100%;
26
+ }
27
+
28
+ .chart-container {
29
+ width: 100%;
30
+ height: 100%;
31
+ }
32
+
33
+ .trend-line {
34
+ fill: none;
35
+ stroke-linecap: round;
36
+ stroke-linejoin: round;
37
+ }
38
+
39
+ .data-point {
40
+ fill: white;
41
+ stroke-width: 2;
42
+ }
43
+
44
+ .axis line,
45
+ .axis path {
46
+ stroke: #ddd;
47
+ }
48
+
49
+ .axis text {
50
+ font-size: 10px;
51
+ fill: #666;
52
+ }
53
+ `
54
+
55
+ render() {
56
+ return html`
57
+ <div class="chart-container">
58
+ <svg
59
+ id="trend-chart"
60
+ width=${this.chartWidth || this.width}
61
+ height=${this.chartHeight || this.height}
62
+ viewBox="0 0 ${this.chartWidth || this.width} ${this.chartHeight || this.height}"
63
+ preserveAspectRatio="xMidYMid meet"
64
+ ></svg>
65
+ </div>
66
+ `
67
+ }
68
+
69
+ connectedCallback() {
70
+ super.connectedCallback()
71
+ this.resizeObserver = new ResizeObserver(entries => {
72
+ for (const entry of entries) {
73
+ const rect = entry.contentRect
74
+ this.chartWidth = rect.width
75
+ this.chartHeight = rect.height
76
+ this.requestUpdate()
77
+ }
78
+ })
79
+ this.resizeObserver.observe(this)
80
+ }
81
+
82
+ disconnectedCallback() {
83
+ this.resizeObserver?.disconnect()
84
+ super.disconnectedCallback()
85
+ }
86
+
87
+ updated() {
88
+ this.drawTrendChart()
89
+ }
90
+
91
+ drawTrendChart() {
92
+ if (!this.data || this.data.length === 0) return
93
+
94
+ const svg = d3.select(this.renderRoot.querySelector('#trend-chart'))
95
+ svg.selectAll('*').remove()
96
+
97
+ const margin = { top: 20, right: 20, bottom: 30, left: 40 }
98
+ const width = this.chartWidth || this.width
99
+ const height = this.chartHeight || this.height
100
+ const chartWidth = width - margin.left - margin.right
101
+ const chartHeight = height - margin.top - margin.bottom
102
+
103
+ // 데이터 파싱
104
+ const parsedData = this.data.map(d => ({
105
+ date: new Date(d[this.dateKey]),
106
+ value: +d[this.valueKey],
107
+ color: d.color || this.lineColor
108
+ }))
109
+
110
+ // 스케일 설정
111
+ const xScale = d3
112
+ .scaleTime()
113
+ .domain(d3.extent(parsedData, d => d.date) as [Date, Date])
114
+ .range([0, chartWidth])
115
+
116
+ const yScale = d3
117
+ .scaleLinear()
118
+ .domain([0, d3.max(parsedData, d => d.value) || 100])
119
+ .range([chartHeight, 0])
120
+
121
+ // SVG 설정
122
+ svg.attr('width', width).attr('height', height)
123
+ const g = svg.append('g').attr('transform', `translate(${margin.left},${margin.top})`)
124
+
125
+ // 축 생성
126
+ const xAxis = d3.axisBottom(xScale).tickFormat(d3.timeFormat('%m/%d')).ticks(5)
127
+
128
+ const yAxis = d3.axisLeft(yScale).ticks(5)
129
+
130
+ g.append('g').attr('class', 'axis').attr('transform', `translate(0,${chartHeight})`).call(xAxis)
131
+
132
+ g.append('g').attr('class', 'axis').call(yAxis)
133
+
134
+ // 라인 생성기
135
+ const line = d3
136
+ .line<{ date: Date; value: number; color: string }>()
137
+ .x(d => xScale(d.date))
138
+ .y(d => yScale(d.value))
139
+ .curve(d3.curveMonotoneX)
140
+
141
+ // 트렌드 라인 그리기
142
+ g.append('path')
143
+ .datum(parsedData)
144
+ .attr('class', 'trend-line')
145
+ .attr('d', line as any)
146
+ .attr('stroke', this.lineColor)
147
+ .attr('stroke-width', this.strokeWidth)
148
+
149
+ // 데이터 포인트 그리기
150
+ if (this.showPoints) {
151
+ g.selectAll('.data-point')
152
+ .data(parsedData)
153
+ .enter()
154
+ .append('circle')
155
+ .attr('class', 'data-point')
156
+ .attr('cx', d => xScale(d.date))
157
+ .attr('cy', d => yScale(d.value))
158
+ .attr('r', this.pointRadius)
159
+ .attr('stroke', d => d.color)
160
+ .attr('fill', 'white')
161
+ }
162
+ }
163
+ }