@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
package/README.md ADDED
@@ -0,0 +1,74 @@
1
+ # KPI 모듈 개요
2
+
3
+ ## 1. KPI란?
4
+
5
+ - KPI(Key Performance Indicator)는 조직의 목표 달성도를 수치로 측정/관리하는 핵심 지표입니다.
6
+ - KPI는 산식(formula), 목표값(target), 카테고리(category), 버전(version), 상태(state) 등 다양한 속성을 가집니다.
7
+ - KPI의 실적값(Performance Value)은 특정 시점/조직/프로젝트별로 기록됩니다.
8
+
9
+ ## 2. KPI 모듈의 주요 엔티티
10
+
11
+ - **Kpi**: KPI 정의(이름, 산식, 카테고리, 상태, 버전 등)
12
+ - **KpiValue**: KPI별 실적값(날짜, 값, 입력방식, 소스 등)
13
+ - **KpiCategory**: KPI 분류(트리 구조 가능)
14
+ - **KpiMetric**: KPI 산식에 사용되는 원천 데이터 항목(코드, 단위, 데이터셋 등)
15
+ - **KpiGrade**: KPI별 등급/구간(구간별 점수, 색상 등)
16
+ - **KpiHistory**: KPI의 버전별 이력(스냅샷)
17
+
18
+ ## 3. Dataset 모듈과의 관계
19
+
20
+ KPI 모듈은 Dataset 모듈의 RAW 데이터를 기반으로 KPI 실적을 산출/관리합니다.
21
+
22
+ ### 3.1 Dataset 모듈이란?
23
+
24
+ - **Dataset**: 다양한 원천 데이터(센서, 수기입력, 외부시스템 등)를 수집/정의/저장하는 모듈
25
+ - **DataSample**: 실제 수집된 데이터 샘플(시간, 값, 입력자 등)
26
+ - **DataSpec, DataKeySet 등**: 데이터 구조, 키, 스펙 정의
27
+
28
+ ### 3.2 KPI와 Dataset의 연결 구조
29
+
30
+ - **KpiMetric ↔ Dataset**: KpiMetric은 KPI 산식에 사용되는 원천 데이터 항목을 정의하며, 각 Metric은 Dataset(원천 데이터셋)의 특정 필드/코드와 연결됩니다.
31
+ - **Kpi.formula**: KPI의 formula는 여러 Metric(코드)와 연산자를 조합한 문자열입니다.
32
+ - **KpiValue의 생성**: KPI 실적값(KpiValue)은 관련 Dataset에서 Metric별 RAW 데이터를 집계/가공하여 formula에 따라 계산된 결과로 생성/저장됩니다.
33
+ - **자동/수동 입력**: 일부 KPI는 Dataset에서 자동 집계, 일부는 수기로 입력 가능합니다.
34
+
35
+ ## 4. 전반적 흐름/관계 요약
36
+
37
+ ```mermaid
38
+ graph TD
39
+ DS[Dataset] -- 원천데이터 --> M[KpiMetric]
40
+ M -- 산식코드 --> K[Kpi]
41
+ K -- 계산/집계 --> V[KpiValue]
42
+ DS -. 수집/입력 .-> S[DataSample]
43
+ ```
44
+
45
+ 1. Dataset에 다양한 원천 데이터가 수집/저장됨
46
+ 2. KpiMetric이 Dataset의 특정 필드/코드를 참조하여 Metric을 정의
47
+ 3. Kpi는 여러 Metric을 조합한 formula로 KPI를 정의
48
+ 4. KpiValue는 특정 시점/조직/조건에 대해 Dataset에서 Metric별 집계 → formula 계산 → KPI 실적값으로 저장
49
+ 5. KpiHistory는 KPI의 버전별 정의/산식/구조 이력을 관리
50
+
51
+ ## 5. 실제 활용 예시
52
+
53
+ ### 5.1 불량률 KPI
54
+
55
+ - Metric: defect_count, total_count (각각 inspection_data Dataset의 필드)
56
+ - Formula: defect_count / total_count \* 100
57
+ - 특정 월/공장/라인별로 Dataset에서 집계 → KPI 실적값 생성
58
+
59
+ ### 5.2 생산성 KPI
60
+
61
+ - Metric: produced_qty, working_hours (각각 생산 Dataset의 필드)
62
+ - Formula: produced_qty / working_hours
63
+
64
+ ## 6. 확장성 및 유연성
65
+
66
+ - KPI 정의, 실적, 이력, 등급 등은 모두 Dataset의 데이터 구조와 밀접하게 연결됩니다.
67
+ - 확장성, 유연성, 이력관리, 자동/수동 입력 등 다양한 KPI 관리 요구를 충족합니다.
68
+
69
+ ---
70
+
71
+ ## 7. 참고 문서
72
+
73
+ - [design-entities.md](./design-entities.md): KPI 엔티티 상세 설계
74
+ - [Dataset 모듈 문서](../dataset/README.md): Dataset 구조 및 활용
@@ -23,7 +23,6 @@ import { KpiImporter } from './kpi-importer'
23
23
 
24
24
  @customElement('kpi-list-page')
25
25
  export class KpiListPage extends connect(store)(localize(i18next)(ScopedElementsMixin(PageView))) {
26
-
27
26
  static styles = [
28
27
  ScrollbarStyles,
29
28
  CommonGristStyles,
@@ -34,8 +33,8 @@ export class KpiListPage extends connect(store)(localize(i18next)(ScopedElements
34
33
 
35
34
  width: 100%;
36
35
 
37
- --grid-record-emphasized-background-color: #8B0000;
38
- --grid-record-emphasized-color: #FF6B6B;
36
+ --grid-record-emphasized-background-color: #8b0000;
37
+ --grid-record-emphasized-color: #ff6b6b;
39
38
  }
40
39
 
41
40
  ox-grist {
@@ -101,11 +100,7 @@ export class KpiListPage extends connect(store)(localize(i18next)(ScopedElements
101
100
  const mode = this.mode || (isMobileDevice() ? 'CARD' : 'GRID')
102
101
 
103
102
  return html`
104
- <ox-grist
105
- .mode=${mode}
106
- .config=${this.gristConfig}
107
- .fetchHandler=${this.fetchHandler.bind(this)}
108
- >
103
+ <ox-grist .mode=${mode} .config=${this.gristConfig} .fetchHandler=${this.fetchHandler.bind(this)}>
109
104
  <div slot="headroom" class="header">
110
105
  <div class="filters">
111
106
  <ox-filters-form autofocus without-search></ox-filters-form>
@@ -121,7 +116,6 @@ export class KpiListPage extends connect(store)(localize(i18next)(ScopedElements
121
116
  <md-icon>add</md-icon>
122
117
  </button>
123
118
  </ox-record-creator>
124
-
125
119
  </div>
126
120
  </div>
127
121
  </ox-grist>
@@ -131,8 +125,42 @@ export class KpiListPage extends connect(store)(localize(i18next)(ScopedElements
131
125
  async pageInitialized(lifecycle: any) {
132
126
  this.gristConfig = {
133
127
  list: {
134
- fields: ['name', 'description'],
135
- details: ['active', 'updatedAt']
128
+ fields: [
129
+ 'name',
130
+ 'description',
131
+ 'category',
132
+ 'formula',
133
+ 'active',
134
+ 'state',
135
+ 'vizType',
136
+ 'schedule',
137
+ 'scheduleId',
138
+ 'timezone',
139
+ 'version',
140
+ 'createdAt',
141
+ 'updatedAt',
142
+ 'creator',
143
+ 'updater',
144
+ 'thumbnail'
145
+ ],
146
+ details: [
147
+ 'name',
148
+ 'description',
149
+ 'category',
150
+ 'formula',
151
+ 'active',
152
+ 'state',
153
+ 'vizType',
154
+ 'schedule',
155
+ 'scheduleId',
156
+ 'timezone',
157
+ 'version',
158
+ 'createdAt',
159
+ 'updatedAt',
160
+ 'creator',
161
+ 'updater',
162
+ 'thumbnail'
163
+ ]
136
164
  },
137
165
  columns: [
138
166
  { type: 'gutter', gutterName: 'sequence' },
@@ -140,56 +168,83 @@ export class KpiListPage extends connect(store)(localize(i18next)(ScopedElements
140
168
  {
141
169
  type: 'string',
142
170
  name: 'name',
143
- header: i18next.t('field.name'),
144
- record: {
145
- editable: true
146
- },
171
+ header: '이름',
172
+ record: { editable: true },
147
173
  filter: 'search',
148
174
  sortable: true,
149
- width: 150
175
+ width: 120
150
176
  },
151
177
  {
152
178
  type: 'string',
153
179
  name: 'description',
154
180
  header: i18next.t('field.description'),
181
+ record: { editable: true },
182
+ filter: 'search',
183
+ width: 200
184
+ },
185
+ // {
186
+ // type: 'string',
187
+ // name: 'category',
188
+ // header: '카테고리',
189
+ // record: { editable: false, renderer: (v, c, r) => r.category?.name },
190
+ // width: 120
191
+ // },
192
+ {
193
+ type: 'resource-object',
194
+ name: 'category',
195
+ label: true,
196
+ header: '카테고리',
155
197
  record: {
156
- editable: true
198
+ editable: true,
199
+ options: {
200
+ title: i18next.t('title.lookup category'),
201
+ queryName: 'kpiCategories'
202
+ }
157
203
  },
158
- filter: 'search',
159
204
  width: 200
160
205
  },
206
+ { type: 'string', name: 'formula', header: '산식', record: { editable: true }, width: 200 },
161
207
  {
162
208
  type: 'checkbox',
163
209
  name: 'active',
164
210
  label: true,
165
211
  header: i18next.t('field.active'),
166
- record: {
167
- editable: true
168
- },
212
+ record: { editable: true },
169
213
  filter: true,
170
214
  sortable: true,
171
215
  width: 60
172
216
  },
217
+ { type: 'string', name: 'state', header: '상태', record: { editable: false }, width: 100 },
218
+ { type: 'string', name: 'vizType', header: '시각화유형', record: { editable: true }, width: 120 },
219
+ { type: 'string', name: 'schedule', header: '스케줄', record: { editable: true }, width: 120 },
220
+ { type: 'string', name: 'scheduleId', header: '스케줄ID', record: { editable: false }, width: 120 },
221
+ { type: 'string', name: 'timezone', header: '타임존', record: { editable: true }, width: 100 },
222
+ { type: 'number', name: 'version', header: '버전', record: { editable: false }, width: 80 },
223
+ { type: 'datetime', name: 'createdAt', header: '생성일', record: { editable: false }, width: 180 },
224
+ {
225
+ type: 'datetime',
226
+ name: 'updatedAt',
227
+ header: i18next.t('field.updated_at'),
228
+ record: { editable: false },
229
+ sortable: true,
230
+ width: 180
231
+ },
232
+ {
233
+ type: 'resource-object',
234
+ name: 'creator',
235
+ header: '생성자',
236
+ record: { editable: false, renderer: (v, c, r) => r.creator?.name },
237
+ width: 120
238
+ },
173
239
  {
174
240
  type: 'resource-object',
175
241
  name: 'updater',
176
242
  header: i18next.t('field.updater'),
177
- record: {
178
- editable: false
179
- },
243
+ record: { editable: false, renderer: (v, c, r) => r.updater?.name },
180
244
  sortable: true,
181
245
  width: 120
182
246
  },
183
- {
184
- type: 'datetime',
185
- name: 'updatedAt',
186
- header: i18next.t('field.updated_at'),
187
- record: {
188
- editable: false
189
- },
190
- sortable: true,
191
- width: 180
192
- }
247
+ { type: 'string', name: 'thumbnail', header: '썸네일', record: { editable: false }, width: 120 }
193
248
  ],
194
249
  rows: {
195
250
  appendable: false,
@@ -197,11 +252,7 @@ export class KpiListPage extends connect(store)(localize(i18next)(ScopedElements
197
252
  multiple: true
198
253
  }
199
254
  },
200
- sorters: [
201
- {
202
- name: 'name'
203
- }
204
- ]
255
+ sorters: [{ name: 'name' }]
205
256
  }
206
257
  }
207
258
 
@@ -221,6 +272,11 @@ export class KpiListPage extends connect(store)(localize(i18next)(ScopedElements
221
272
  name
222
273
  description
223
274
  active
275
+ category {
276
+ id
277
+ name
278
+ description
279
+ }
224
280
  updater {
225
281
  id
226
282
  name
@@ -355,12 +411,7 @@ export class KpiListPage extends connect(store)(localize(i18next)(ScopedElements
355
411
 
356
412
  async exportHandler() {
357
413
  const exportTargets = this.grist.selected.length ? this.grist.selected : this.grist.dirtyData.records
358
- const targetFieldSet = new Set([
359
- 'id',
360
- 'name',
361
- 'description',
362
- 'active'
363
- ])
414
+ const targetFieldSet = new Set(['id', 'name', 'description', 'active'])
364
415
 
365
416
  return exportTargets.map(kpi => {
366
417
  let tempObj = {}
@@ -389,10 +440,9 @@ export class KpiListPage extends connect(store)(localize(i18next)(ScopedElements
389
440
  title: i18next.t('title.import kpi')
390
441
  }
391
442
  )
392
-
443
+
393
444
  popup.onclosed = () => {
394
445
  this.grist.fetch()
395
446
  }
396
447
  }
397
448
  }
398
-
@@ -0,0 +1,260 @@
1
+ import { html, css, LitElement } from 'lit'
2
+ import { customElement, state } from 'lit/decorators.js'
3
+ import { marked } from 'marked'
4
+
5
+ import { PageView, store } from '@operato/shell'
6
+ import { i18next, localize } from '@operato/i18n'
7
+
8
+ @customElement('kpi-overview')
9
+ export class KpiOverview extends PageView {
10
+ static styles = css`
11
+ .overview-container {
12
+ display: flex;
13
+ gap: 32px;
14
+ background: #f7f5fa;
15
+ border-radius: 20px;
16
+ padding: 32px;
17
+ margin-bottom: 24px;
18
+ align-items: stretch;
19
+ }
20
+ .overview-left {
21
+ flex: 1;
22
+ display: flex;
23
+ flex-direction: column;
24
+ justify-content: center;
25
+ }
26
+ .overview-title {
27
+ font-size: 2.8rem;
28
+ font-weight: bold;
29
+ margin-bottom: 24px;
30
+ }
31
+ .overview-desc {
32
+ font-size: 1.2rem;
33
+ color: #222;
34
+ line-height: 1.6;
35
+ }
36
+ .overview-right {
37
+ flex: 1;
38
+ display: flex;
39
+ align-items: center;
40
+ justify-content: center;
41
+ }
42
+ .overview-img {
43
+ width: 100%;
44
+ max-width: 420px;
45
+ border-radius: 12px;
46
+ object-fit: cover;
47
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
48
+ }
49
+ .group-tabs {
50
+ display: flex;
51
+ gap: 8px;
52
+ margin: 0 0 16px 0;
53
+ justify-content: center;
54
+ }
55
+ .group-tab {
56
+ padding: 10px 28px;
57
+ border-radius: 16px 16px 0 0;
58
+ background: #eee;
59
+ cursor: pointer;
60
+ font-size: 1.1rem;
61
+ font-weight: 500;
62
+ border: none;
63
+ outline: none;
64
+ transition: background 0.2s;
65
+ }
66
+ .group-tab[selected] {
67
+ background: #d6d6f7;
68
+ color: #222;
69
+ font-weight: bold;
70
+ }
71
+ .kpi-list {
72
+ display: flex;
73
+ gap: 8px;
74
+ margin: 0 0 32px 0;
75
+ justify-content: center;
76
+ }
77
+ .kpi-item {
78
+ padding: 8px 20px;
79
+ border-radius: 12px;
80
+ background: #f5f5f5;
81
+ cursor: pointer;
82
+ font-size: 1rem;
83
+ border: none;
84
+ outline: none;
85
+ transition: background 0.2s;
86
+ }
87
+ .kpi-item[selected] {
88
+ background: #bdf;
89
+ color: #222;
90
+ font-weight: bold;
91
+ }
92
+ .main-content {
93
+ display: flex;
94
+ gap: 32px;
95
+ margin-top: 32px;
96
+ align-items: flex-start;
97
+ justify-content: center;
98
+ }
99
+ .markdown {
100
+ background: #fff;
101
+ border-radius: 12px;
102
+ padding: 40px 32px;
103
+ min-height: 240px;
104
+ flex: 2;
105
+ font-size: 2rem;
106
+ color: #222;
107
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
108
+ }
109
+ .toc {
110
+ flex: 1;
111
+ background: #faf9fd;
112
+ border-radius: 12px;
113
+ padding: 32px 24px;
114
+ font-size: 1.1rem;
115
+ color: #444;
116
+ min-width: 220px;
117
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03);
118
+ }
119
+ .toc-title {
120
+ font-weight: bold;
121
+ margin-bottom: 12px;
122
+ font-size: 1.2rem;
123
+ }
124
+ .toc-list {
125
+ margin: 0 0 16px 0;
126
+ padding: 0;
127
+ list-style: none;
128
+ }
129
+ .toc-list li {
130
+ margin-bottom: 6px;
131
+ }
132
+ .toc-btn {
133
+ display: block;
134
+ margin: 12px 0;
135
+ padding: 8px 16px;
136
+ border: 1px solid #aaa;
137
+ border-radius: 8px;
138
+ background: #fff;
139
+ color: #333;
140
+ cursor: pointer;
141
+ font-size: 1rem;
142
+ text-align: left;
143
+ transition: background 0.2s;
144
+ }
145
+ .toc-btn:hover {
146
+ background: #f0f0f0;
147
+ }
148
+ `
149
+
150
+ get context() {
151
+ return {
152
+ title: i18next.t('title.kpi overview')
153
+ }
154
+ }
155
+
156
+ @state() selectedGroup = 0
157
+ @state() selectedKpi = 0
158
+
159
+ kpiGroups = [
160
+ {
161
+ name: '일정성과 지표',
162
+ kpis: ['연면적 대비 공사기간', '설계변경에 따른 공기 증감률', '일정 이탈 수준', '일정성과 수준 평가']
163
+ },
164
+ {
165
+ name: '비용성과 지표',
166
+ kpis: ['연면적 대비 공사비', '설계변경에 따른 공사비 증감률', '비용성과 수준 평가']
167
+ },
168
+ {
169
+ name: '품질성과 지표',
170
+ kpis: ['품질시험 불합격률', '검수재 불합격률', '검측 불합격률', '품질 SL-PA 결과값', '품질성과 수준 평가']
171
+ },
172
+ {
173
+ name: '안전성과 지표',
174
+ kpis: ['재해율', '재해강도율', '안전 SL-PA 결과값', '안전성과 수준 평가']
175
+ },
176
+ {
177
+ name: '환경성과 지표',
178
+ kpis: ['건설폐기물 발생량', '환경성과 수준 평가']
179
+ },
180
+ {
181
+ name: '생산성과 지표',
182
+ kpis: ['투입인력 생산성', '생산성과 수준 평가']
183
+ }
184
+ ]
185
+
186
+ kpiDescriptions = {
187
+ '연면적 대비 공사기간': `# 연면적 대비 공사기간\n- 설명: 프로젝트의 전체 연면적 대비 실제 소요된 공사기간을 측정합니다.\n- 산식: 실제공사기간(개월) / (연면적/1000m2)\n- 데이터 표준형: 실제공사기간(개월) / (연면적/1000m2)\n- 활용: 대형 프로젝트의 일정 효율성 파악`,
188
+ '설계변경에 따른 공기 증감률': `# 설계변경에 따른 공기 증감률\n- 설명: 설계변경으로 인한 공기 단축 또는 증가 효과를 측정합니다.\n- 산식: (실제공사기간 - 계획공사기간) / 계획공사기간(개월)\n- 데이터 표준형: (실제공사기간 - 계획공사기간)(개월) / 계획공사기간(개월)`,
189
+ '일정 이탈 수준': `# 일정 이탈 수준\n- 설명: 계획 대비 실제 일정의 이탈 정도를 평가합니다.\n- 산식: Table(DART 등) 활용\n- 평가 방식: 1~5점 척도(감리자 평가)`,
190
+ '일정성과 수준 평가': `# 일정성과 수준 평가\n- 설명: 일정성과의 전반적인 수준을 감리자가 평가합니다.\n- 평가 방식: 감리자 1~5점 척도`,
191
+ '연면적 대비 공사비': `# 연면적 대비 공사비\n- 설명: 프로젝트의 전체 연면적 대비 실제 소요된 공사비를 측정합니다.\n- 산식: 실제공사비(억원) / (연면적/1000m2)\n- 데이터 표준형: 실제공사비(억원) / (연면적/1000m2)`,
192
+ '설계변경에 따른 공사비 증감률': `# 설계변경에 따른 공사비 증감률\n- 설명: 설계변경으로 인한 공사비 증감률을 측정합니다.\n- 산식: (실제공사비 - 계획공사비) / 계획공사비(억원)\n- 데이터 표준형: (실제공사비 - 계획공사비)(억원) / 계획공사비(억원)`,
193
+ '비용성과 수준 평가': `# 비용성과 수준 평가\n- 설명: 비용성과의 전반적인 수준을 감리자가 평가합니다.\n- 평가 방식: 감리자 1~5점 척도`,
194
+ '품질시험 불합격률': `# 품질시험 불합격률\n- 설명: 전체 품질시험 중 불합격 건수의 비율을 측정합니다.\n- 산식: 품질시험 불합격 건수(건) / (연면적/1000m2)`,
195
+ '검수재 불합격률': `# 검수재 불합격률\n- 설명: 검수재 중 불합격 건수의 비율을 측정합니다.\n- 산식: 검수재 불합격 건수(건) / (연면적/1000m2)`,
196
+ '검측 불합격률': `# 검측 불합격률\n- 설명: 전체 검측 중 불합격 건수의 비율을 측정합니다.\n- 산식: 품질검측 불합격 건수(건) / 품질검측 건수(건)`,
197
+ '품질 SL-PA 결과값': `# 품질 SL-PA 결과값\n- 설명: SL-PA(Safety Level - Performance Assessment)는 건설/안전 분야에서 안전관리 수준을 정량적으로 평가하는 대표 지표입니다.\n- 평가 방식: Python 활용 1~5점 척도`,
198
+ '품질성과 수준 평가': `# 품질성과 수준 평가\n- 설명: 품질성과의 전반적인 수준을 감리자가 평가합니다.\n- 평가 방식: 감리자 1~5점 척도`,
199
+ 재해율: `# 재해율\n- 설명: 연간 근로자 수 대비 재해 발생 건수의 비율을 측정합니다.\n- 산식: (재해 건수(건) / 연간 근로자 수(명)) * 100`,
200
+ 재해강도율: `# 재해강도율\n- 설명: 총 근로자 수와 연간 근로시간 대비 노동손실일수의 비율을 측정합니다.\n- 산식: (노동손실일수(일) / (총 근로자 수 * 1인당 연간 근로시간)) * 100`,
201
+ '안전 SL-PA 결과값': `# 안전 SL-PA 결과값\n- 설명: SL-PA(Safety Level - Performance Assessment)는 건설/안전 분야에서 **안전관리 수준**을 정량적으로 평가하는 대표 지표입니다.\n- 활용: 현장의 안전성과를 수치로 진단하고, 위험요소 개선 및 안전관리 정책 수립에 활용됩니다.\n- 평가 방식: Python 활용 1~5점 척도 (1점: 매우 미흡, 5점: 매우 우수)`,
202
+ '안전성과 수준 평가': `# 안전성과 수준 평가\n- 설명: 안전성과의 전반적인 수준을 감리자가 평가합니다.\n- 평가 방식: 감리자 1~5점 척도`,
203
+ '건설폐기물 발생량': `# 건설폐기물 발생량\n- 설명: 전체 건설폐기물 발생량을 측정합니다.\n- 산식: 총 건설 폐기물 발생량(ton) / (연면적/1000m2)`,
204
+ '환경성과 수준 평가': `# 환경성과 수준 평가\n- 설명: 환경성과의 전반적인 수준을 감리자가 평가합니다.\n- 평가 방식: 감리자 1~5점 척도`,
205
+ '투입인력 생산성': `# 투입인력 생산성\n- 설명: 투입 인력 대비 생산성을 측정합니다.\n- 산식: 투입인력(명) / (연면적/1000m2)`,
206
+ '생산성과 수준 평가': `# 생산성과 수준 평가\n- 설명: 생산성과의 전반적인 수준을 감리자가 평가합니다.\n- 평가 방식: 감리자 1~5점 척도`
207
+ }
208
+
209
+ render() {
210
+ const group = this.kpiGroups[this.selectedGroup]
211
+ const kpi = group.kpis[this.selectedKpi]
212
+ const md = this.kpiDescriptions[kpi] || '각 지표의 주요 소개 내용을 마크다운으로 입력하세요.'
213
+
214
+ return html`
215
+ <div class="overview-container">
216
+ <div class="overview-left">
217
+ <div class="overview-title">KPI개요</div>
218
+ <div class="overview-desc">
219
+ 건설현장 KPI 관리 시스템은 프로젝트 성과를 측정하고 모니터링하여 효율적인 공정 관리를 지원합니다.<br />
220
+ 주요 지표를 통해 현장 생산성, 품질, 안전 등을 실시간으로 파악할 수 있습니다.
221
+ </div>
222
+ </div>
223
+ <div class="overview-right">
224
+ <img
225
+ class="overview-img"
226
+ src="https://images.unsplash.com/photo-1464983953574-0892a716854b?auto=format&fit=crop&w=600&q=80"
227
+ alt="건설현장 안전"
228
+ />
229
+ </div>
230
+ </div>
231
+ <div class="group-tabs">
232
+ ${this.kpiGroups.map(
233
+ (g, i) => html`
234
+ <button
235
+ class="group-tab"
236
+ ?selected=${i === this.selectedGroup}
237
+ @click=${() => {
238
+ this.selectedGroup = i
239
+ this.selectedKpi = 0
240
+ }}
241
+ >
242
+ ${g.name}
243
+ </button>
244
+ `
245
+ )}
246
+ </div>
247
+ <div class="main-content">
248
+ <div class="markdown" .innerHTML=${marked(md)}></div>
249
+ <div class="toc">
250
+ <div class="toc-title">목차</div>
251
+ <ul class="toc-list">
252
+ ${group.kpis.map(
253
+ (k, i) => html` <li><button class="toc-btn" @click=${() => (this.selectedKpi = i)}>${k}</button></li> `
254
+ )}
255
+ </ul>
256
+ </div>
257
+ </div>
258
+ `
259
+ }
260
+ }