@dssp/dkpi 1.0.0-alpha.8 → 1.0.0-alpha.81

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 (252) hide show
  1. package/KPI-STATISTICS-SERVICE.md +233 -0
  2. package/_index.html +0 -5
  3. package/assets/favicon.ico +0 -0
  4. package/assets/images/project-image.png +0 -0
  5. package/assets/manifest/apple-1024.png +0 -0
  6. package/assets/manifest/apple-120.png +0 -0
  7. package/assets/manifest/apple-152.png +0 -0
  8. package/assets/manifest/apple-167.png +0 -0
  9. package/assets/manifest/apple-180.png +0 -0
  10. package/assets/manifest/apple-touch-icon.png +0 -0
  11. package/assets/manifest/badge-128x128.png +0 -0
  12. package/assets/manifest/chrome-splashscreen-icon-384x384.png +0 -0
  13. package/assets/manifest/chrome-touch-icon-192x192.png +0 -0
  14. package/assets/manifest/icon-128x128.png +0 -0
  15. package/assets/manifest/icon-192x192.png +0 -0
  16. package/assets/manifest/icon-512x512.png +0 -0
  17. package/assets/manifest/icon-72x72.png +0 -0
  18. package/assets/manifest/icon-96x96.png +0 -0
  19. package/assets/manifest/image-metaog.png +0 -0
  20. package/assets/manifest/maskable_icon.png +0 -0
  21. package/assets/manifest/ms-icon-144x144.png +0 -0
  22. package/assets/manifest/ms-touch-icon-144x144-precomposed.png +0 -0
  23. package/assets/videos/intro.mp4 +0 -0
  24. package/config/config.development.js +2 -1
  25. package/config/config.production.js +2 -1
  26. package/dist-client/bootstrap.d.ts +1 -0
  27. package/dist-client/bootstrap.js +75 -4
  28. package/dist-client/bootstrap.js.map +1 -1
  29. package/dist-client/components/kpi-2d-lookup-chart.d.ts +43 -0
  30. package/dist-client/components/kpi-2d-lookup-chart.js +253 -0
  31. package/dist-client/components/kpi-2d-lookup-chart.js.map +1 -0
  32. package/dist-client/components/kpi-boxplot-chart.d.ts +24 -0
  33. package/dist-client/components/kpi-boxplot-chart.js +291 -0
  34. package/dist-client/components/kpi-boxplot-chart.js.map +1 -0
  35. package/dist-client/components/kpi-lookup-chart.d.ts +53 -0
  36. package/dist-client/components/kpi-lookup-chart.js +430 -0
  37. package/dist-client/components/kpi-lookup-chart.js.map +1 -0
  38. package/dist-client/components/kpi-mini-trend-chart.d.ts +14 -0
  39. package/dist-client/components/kpi-mini-trend-chart.js +148 -0
  40. package/dist-client/components/kpi-mini-trend-chart.js.map +1 -0
  41. package/dist-client/components/kpi-radar-chart.d.ts +17 -0
  42. package/dist-client/components/kpi-radar-chart.js +259 -0
  43. package/dist-client/components/kpi-radar-chart.js.map +1 -0
  44. package/dist-client/components/kpi-single-boxplot-chart.d.ts +25 -0
  45. package/dist-client/components/kpi-single-boxplot-chart.js +398 -0
  46. package/dist-client/components/kpi-single-boxplot-chart.js.map +1 -0
  47. package/dist-client/components/kpi-trend-chart.d.ts +25 -0
  48. package/dist-client/components/kpi-trend-chart.js +220 -0
  49. package/dist-client/components/kpi-trend-chart.js.map +1 -0
  50. package/dist-client/components/sv-pagenation-control.d.ts +18 -0
  51. package/dist-client/components/sv-pagenation-control.js +142 -0
  52. package/dist-client/components/sv-pagenation-control.js.map +1 -0
  53. package/dist-client/entries/auth/checkin.d.ts +38 -0
  54. package/dist-client/entries/auth/checkin.js +546 -0
  55. package/dist-client/entries/auth/checkin.js.map +1 -0
  56. package/dist-client/google-map/common-google-map.d.ts +35 -0
  57. package/dist-client/google-map/common-google-map.js +345 -0
  58. package/dist-client/google-map/common-google-map.js.map +1 -0
  59. package/dist-client/google-map/google-map-loader.d.ts +6 -0
  60. package/dist-client/google-map/google-map-loader.js +23 -0
  61. package/dist-client/google-map/google-map-loader.js.map +1 -0
  62. package/dist-client/icons/menu-icons.d.ts +6 -0
  63. package/dist-client/icons/menu-icons.js +42 -0
  64. package/dist-client/icons/menu-icons.js.map +1 -1
  65. package/dist-client/pages/component/project-update-header.d.ts +1 -0
  66. package/dist-client/pages/component/project-update-header.js +127 -0
  67. package/dist-client/pages/component/project-update-header.js.map +1 -0
  68. package/dist-client/pages/kpi-admin/dssp-kpi-list-page.d.ts +22 -0
  69. package/dist-client/pages/kpi-admin/dssp-kpi-list-page.js +57 -0
  70. package/dist-client/pages/kpi-admin/dssp-kpi-list-page.js.map +1 -0
  71. package/dist-client/pages/kpi-admin/dssp-kpi-overview.d.ts +46 -0
  72. package/dist-client/pages/kpi-admin/dssp-kpi-overview.js +378 -0
  73. package/dist-client/pages/kpi-admin/dssp-kpi-overview.js.map +1 -0
  74. package/dist-client/pages/kpi-admin/kpi-grade-2d-editor.d.ts +20 -0
  75. package/dist-client/pages/kpi-admin/kpi-grade-2d-editor.js +445 -0
  76. package/dist-client/pages/kpi-admin/kpi-grade-2d-editor.js.map +1 -0
  77. package/dist-client/pages/kpi-admin/kpi-system-guide.d.ts +18 -0
  78. package/dist-client/pages/kpi-admin/kpi-system-guide.js +535 -0
  79. package/dist-client/pages/kpi-admin/kpi-system-guide.js.map +1 -0
  80. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.d.ts +18 -0
  81. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js +259 -0
  82. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js.map +1 -0
  83. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.d.ts +22 -0
  84. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js +346 -0
  85. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js.map +1 -0
  86. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.d.ts +26 -0
  87. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js +433 -0
  88. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js.map +1 -0
  89. package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.d.ts +8 -0
  90. package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.js +78 -0
  91. package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.js.map +1 -0
  92. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.d.ts +38 -0
  93. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js +851 -0
  94. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js.map +1 -0
  95. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.d.ts +42 -0
  96. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js +316 -0
  97. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js.map +1 -0
  98. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.d.ts +28 -0
  99. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js +497 -0
  100. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js.map +1 -0
  101. package/dist-client/pages/kpi-dashboard/kpi-alert-panel.d.ts +18 -0
  102. package/dist-client/pages/kpi-dashboard/kpi-alert-panel.js +131 -0
  103. package/dist-client/pages/kpi-dashboard/kpi-alert-panel.js.map +1 -0
  104. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.d.ts +52 -0
  105. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js +798 -0
  106. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js.map +1 -0
  107. package/dist-client/pages/kpi-dashboard/kpi-dashboard.d.ts +63 -0
  108. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js +1089 -0
  109. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js.map +1 -0
  110. package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.d.ts +12 -0
  111. package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.js +82 -0
  112. package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.js.map +1 -0
  113. package/dist-client/pages/kpi-dashboard/kpi-history-viewer.d.ts +11 -0
  114. package/dist-client/pages/kpi-dashboard/kpi-history-viewer.js +65 -0
  115. package/dist-client/pages/kpi-dashboard/kpi-history-viewer.js.map +1 -0
  116. package/dist-client/pages/kpi-dashboard/kpi-list-summary.d.ts +13 -0
  117. package/dist-client/pages/kpi-dashboard/kpi-list-summary.js +115 -0
  118. package/dist-client/pages/kpi-dashboard/kpi-list-summary.js.map +1 -0
  119. package/dist-client/pages/kpi-dashboard/kpi-performance-summary.d.ts +15 -0
  120. package/dist-client/pages/kpi-dashboard/kpi-performance-summary.js +147 -0
  121. package/dist-client/pages/kpi-dashboard/kpi-performance-summary.js.map +1 -0
  122. package/dist-client/pages/kpi-dashboard/kpi-value-entry.d.ts +7 -0
  123. package/dist-client/pages/kpi-dashboard/kpi-value-entry.js +86 -0
  124. package/dist-client/pages/kpi-dashboard/kpi-value-entry.js.map +1 -0
  125. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.d.ts +57 -0
  126. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js +719 -0
  127. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js.map +1 -0
  128. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.d.ts +23 -0
  129. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js +76 -0
  130. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js.map +1 -0
  131. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.d.ts +68 -0
  132. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js +394 -0
  133. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js.map +1 -0
  134. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.d.ts +12 -0
  135. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js +174 -0
  136. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js.map +1 -0
  137. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.d.ts +40 -0
  138. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js +190 -0
  139. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js.map +1 -0
  140. package/dist-client/pages/kpi-value/kpi-value-importer.d.ts +23 -0
  141. package/dist-client/pages/kpi-value/kpi-value-importer.js +93 -0
  142. package/dist-client/pages/kpi-value/kpi-value-importer.js.map +1 -0
  143. package/dist-client/pages/kpi-value/kpi-value-list-page.d.ts +71 -0
  144. package/dist-client/pages/kpi-value/kpi-value-list-page.js +454 -0
  145. package/dist-client/pages/kpi-value/kpi-value-list-page.js.map +1 -0
  146. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.d.ts +61 -0
  147. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js +766 -0
  148. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js.map +1 -0
  149. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.d.ts +16 -0
  150. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js +341 -0
  151. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js.map +1 -0
  152. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.d.ts +21 -0
  153. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.js +335 -0
  154. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.js.map +1 -0
  155. package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.d.ts +42 -0
  156. package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.js +641 -0
  157. package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.js.map +1 -0
  158. package/dist-client/pages/sv-project-complete.d.ts +25 -0
  159. package/dist-client/pages/sv-project-complete.js +263 -0
  160. package/dist-client/pages/sv-project-complete.js.map +1 -0
  161. package/dist-client/pages/sv-project-completed-list.d.ts +27 -0
  162. package/dist-client/pages/sv-project-completed-list.js +416 -0
  163. package/dist-client/pages/sv-project-completed-list.js.map +1 -0
  164. package/dist-client/pages/sv-project-detail.d.ts +58 -0
  165. package/dist-client/pages/sv-project-detail.js +1418 -0
  166. package/dist-client/pages/sv-project-detail.js.map +1 -0
  167. package/dist-client/pages/sv-project-list.d.ts +174 -0
  168. package/dist-client/pages/sv-project-list.js +578 -0
  169. package/dist-client/pages/sv-project-list.js.map +1 -0
  170. package/dist-client/pages/sv-project-update.d.ts +86 -0
  171. package/dist-client/pages/sv-project-update.js +1331 -0
  172. package/dist-client/pages/sv-project-update.js.map +1 -0
  173. package/dist-client/pages/sv-user-management.d.ts +1 -0
  174. package/dist-client/pages/sv-user-management.js +5 -0
  175. package/dist-client/pages/sv-user-management.js.map +1 -1
  176. package/dist-client/route.d.ts +1 -1
  177. package/dist-client/route.js +50 -2
  178. package/dist-client/route.js.map +1 -1
  179. package/dist-client/shared/complete-api.d.ts +37 -0
  180. package/dist-client/shared/complete-api.js +224 -0
  181. package/dist-client/shared/complete-api.js.map +1 -0
  182. package/dist-client/shared/func.d.ts +2 -0
  183. package/dist-client/shared/func.js +22 -0
  184. package/dist-client/shared/func.js.map +1 -0
  185. package/dist-client/shared/integration-fetch.d.ts +35 -0
  186. package/dist-client/shared/integration-fetch.js +53 -0
  187. package/dist-client/shared/integration-fetch.js.map +1 -0
  188. package/dist-client/themes/dark.css +24 -24
  189. package/dist-client/themes/light.css +23 -23
  190. package/dist-client/tsconfig.tsbuildinfo +1 -1
  191. package/dist-client/viewparts/menu-tools.d.ts +40 -5
  192. package/dist-client/viewparts/menu-tools.js +292 -43
  193. package/dist-client/viewparts/menu-tools.js.map +1 -1
  194. package/dist-server/index.d.ts +2 -0
  195. package/dist-server/index.js +5 -0
  196. package/dist-server/index.js.map +1 -1
  197. package/dist-server/migrations/index.d.ts +1 -0
  198. package/dist-server/migrations/index.js +12 -0
  199. package/dist-server/migrations/index.js.map +1 -0
  200. package/dist-server/scripts/calculate-kpi-scores.d.ts +10 -0
  201. package/dist-server/scripts/calculate-kpi-scores.js +333 -0
  202. package/dist-server/scripts/calculate-kpi-scores.js.map +1 -0
  203. package/dist-server/scripts/load-grade-data-migration.d.ts +14 -0
  204. package/dist-server/scripts/load-grade-data-migration.js +279 -0
  205. package/dist-server/scripts/load-grade-data-migration.js.map +1 -0
  206. package/dist-server/scripts/propagate-parent-kpi-values.d.ts +14 -0
  207. package/dist-server/scripts/propagate-parent-kpi-values.js +786 -0
  208. package/dist-server/scripts/propagate-parent-kpi-values.js.map +1 -0
  209. package/dist-server/scripts/recalculate-by-project-name.d.ts +2 -0
  210. package/dist-server/scripts/recalculate-by-project-name.js +72 -0
  211. package/dist-server/scripts/recalculate-by-project-name.js.map +1 -0
  212. package/dist-server/service/index.d.ts +4 -0
  213. package/dist-server/service/index.js +20 -0
  214. package/dist-server/service/index.js.map +1 -0
  215. package/dist-server/service/kpi-metric-value/index.d.ts +4 -0
  216. package/dist-server/service/kpi-metric-value/index.js +8 -0
  217. package/dist-server/service/kpi-metric-value/index.js.map +1 -0
  218. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.d.ts +166 -0
  219. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js +951 -0
  220. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js.map +1 -0
  221. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.d.ts +7 -0
  222. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js +59 -0
  223. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js.map +1 -0
  224. package/dist-server/service/kpi-stat/index.d.ts +4 -0
  225. package/dist-server/service/kpi-stat/index.js +8 -0
  226. package/dist-server/service/kpi-stat/index.js.map +1 -0
  227. package/dist-server/service/kpi-stat/kpi-stat-query.d.ts +12 -0
  228. package/dist-server/service/kpi-stat/kpi-stat-query.js +663 -0
  229. package/dist-server/service/kpi-stat/kpi-stat-query.js.map +1 -0
  230. package/dist-server/service/kpi-stat/kpi-stat-types.d.ts +32 -0
  231. package/dist-server/service/kpi-stat/kpi-stat-types.js +180 -0
  232. package/dist-server/service/kpi-stat/kpi-stat-types.js.map +1 -0
  233. package/dist-server/service/kpi-value/index.d.ts +3 -0
  234. package/dist-server/service/kpi-value/index.js +7 -0
  235. package/dist-server/service/kpi-value/index.js.map +1 -0
  236. package/dist-server/service/kpi-value/kpi-value-query.d.ts +8 -0
  237. package/dist-server/service/kpi-value/kpi-value-query.js +63 -0
  238. package/dist-server/service/kpi-value/kpi-value-query.js.map +1 -0
  239. package/dist-server/tsconfig.tsbuildinfo +1 -1
  240. package/kpi-module-service-tests.md +1286 -0
  241. package/kpi-module-test-report.md +676 -0
  242. package/kpi-module-unit-test-detailed-report.md +925 -0
  243. package/kpi-module-unit-tests-detailed.md +1452 -0
  244. package/package.json +69 -55
  245. package/recalculate-batch.sh +64 -0
  246. package/recalculate-projects-range.sh +98 -0
  247. package/schema.graphql +3423 -462
  248. package/things-factory.config.js +12 -1
  249. package/translations/en.json +5 -1
  250. package/translations/ko.json +5 -1
  251. package/views/auth-page.html +0 -1
  252. package/views/public/home.html +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dssp-kpi-overview.js","sourceRoot":"","sources":["../../../client/pages/kpi-admin/dssp-kpi-overview.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, css, nothing, CSSResultGroup } from 'lit'\nimport { customElement } from 'lit/decorators.js'\nimport { client } from '@operato/graphql'\nimport gql from 'graphql-tag'\nimport { KpiOverview } from '@things-factory/kpi/dist-client/pages/kpi/kpi-overview.js'\n\nimport '../../components/kpi-lookup-chart'\nimport '../../components/kpi-2d-lookup-chart'\n\nconst GET_KPI_OVERVIEW_EXT = gql`\n query {\n kpiCategories: kpisLevel1 {\n id\n name\n description\n formula\n active\n kpis: children {\n id\n name\n description\n formula\n active\n grades\n scoreType\n valueType\n }\n }\n }\n`\n\n/**\n * KPI scoreType 정의 — value → score 변환 방식\n *\n * NONE — 미설정 (부모 KPI 등)\n * DIRECT — value = score, 별도 변환 없음 (Y/Z: formula 결과가 곧 score)\n * FORMULA — scoreFormula로 value → score 변환\n * LOOKUP — grade table(1D)로 raw value → 0~1 score 변환 (연속값 KPI)\n * 예: X11(연면적 대비 공사기간), X33(검측 불합격률), X34(SL-PA)\n * → 성과 분포 커브 차트로 표시\n * CUSTOM — 2D 룩업 등 특수 구조의 grade (다차원 입력 → 점수)\n * 예: X13(일정 이탈 수준) = 공정률 × 편차율 → 1~5점\n * → 히트맵 차트로 표시\n * ASSESSMENT — 감리자 직접 평가, value(1~5) = score (grade table 불필요)\n * 예: X14(일정성과 수준 평가), X23(비용성과), X35(품질성과) 등\n * → 등급 기준 설명만 표시 (차트 없음)\n *\n * Note: DIRECT와 ASSESSMENT는 모두 value=score이지만,\n * ASSESSMENT는 감리자가 1~5점을 직접 입력하는 정성 평가를 의미하고,\n * DIRECT는 산식 등으로 자동 계산된 value가 변환 없이 score가 되는 것을 의미한다.\n */\n\n/**\n * KPI 개요 페이지 — 등급 구간을 성과 분포 차트로 표현\n *\n * - 0~1 스케일 KPI: 성과 분포 커브 (kpi-lookup-chart)\n * - 1~5 평가형 KPI: 5단계 세그먼트 게이지 (kpi-lookup-chart 자동 감지)\n * - X13 2D 룩업: 공정률×편차율 히트맵 (kpi-2d-lookup-chart)\n */\n@customElement('dssp-kpi-overview')\n// @ts-ignore styles type override\nexport class DsspKpiOverview extends KpiOverview {\n static styles = [\n KpiOverview.styles,\n css`\n :host {\n overflow-y: auto;\n }\n .grades-section {\n margin-top: 16px;\n background: #fff;\n border-radius: 8px;\n padding: 20px;\n border: 2px solid #0c4da2;\n }\n .grades-title {\n font-size: 1rem;\n font-weight: 700;\n color: #333;\n margin-bottom: 12px;\n }\n .grades-chart {\n width: 100%;\n height: 320px;\n }\n .grade-descriptions {\n display: flex;\n flex-direction: column;\n gap: 6px;\n margin-top: 12px;\n }\n .grade-chip {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n border-radius: 20px;\n font-size: 0.8rem;\n font-weight: 500;\n background: #f5f5f5;\n border: 1px solid #e0e0e0;\n }\n .grade-chip .dot {\n width: 10px;\n height: 10px;\n border-radius: 50%;\n flex-shrink: 0;\n }\n .grade-chip .score {\n font-weight: 700;\n min-width: 24px;\n }\n .grade-chip .desc {\n color: #555;\n }\n .type-info {\n margin-top: 16px;\n display: flex;\n gap: 8px;\n flex-wrap: wrap;\n }\n .type-badge {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 5px 10px;\n border-radius: 6px;\n font-size: 0.75rem;\n border: 1px solid #e0e0e0;\n background: #fafafa;\n }\n .type-badge .label {\n color: #888;\n font-weight: 400;\n }\n .type-badge .value {\n font-weight: 600;\n color: #333;\n }\n .x-section {\n display: flex;\n gap: 0;\n margin-top: 12px;\n background: #fff;\n border: 2px solid #0c4da2;\n border-top: none;\n overflow: hidden;\n }\n .x-detail {\n flex: 0 0 320px;\n padding: 20px 25px;\n border-right: 1px solid rgba(46, 164, 223, 0.1);\n font-size: 0.875rem;\n color: #212529;\n }\n .x-chart {\n flex: 1;\n padding: 16px;\n min-height: 280px;\n }\n .y-info {\n background: linear-gradient(135deg, #f0f7ff 0%, #e8f4f8 100%);\n border-radius: 8px;\n padding: 16px 20px;\n margin-bottom: 16px;\n border-left: 4px solid #0c4da2;\n }\n .y-name {\n font-size: 1rem;\n font-weight: 700;\n color: #0c4da2;\n margin-bottom: 6px;\n }\n .y-desc {\n font-size: 0.875rem;\n color: #444;\n line-height: 1.6;\n margin-bottom: 8px;\n }\n .y-formula {\n font-size: 0.85rem;\n font-weight: 600;\n color: #333;\n background: rgba(255,255,255,0.7);\n padding: 8px 12px;\n border-radius: 4px;\n font-family: monospace;\n }\n .y-formula-label {\n font-size: 0.75rem;\n color: #888;\n margin-bottom: 4px;\n }\n `\n ]\n\n override async firstUpdated() {\n try {\n const { data } = await client.query({ query: GET_KPI_OVERVIEW_EXT })\n this.kpiCategories = data.kpiCategories\n this.loading = false\n } catch (e) {\n this.error = e instanceof Error ? e : new Error(String(e))\n this.loading = false\n }\n }\n\n override render() {\n if (this.error) return html`<div>에러: ${this.error?.message}</div>`\n if (!this.kpiCategories.length) return html`<div>KPI 카테고리가 없습니다.</div>`\n\n const selctedY = this.kpiCategories?.[this.selectedGroup] || ''\n const groupRaw = this.kpiCategories[this.selectedGroup] as any\n // active가 false인 KPI 제외 (X31, X32 등 삭제된 지표)\n const activeKpis = groupRaw?.kpis?.filter((k: any) => k.active !== false) || []\n const group = { ...groupRaw, kpis: activeKpis }\n const kpi = group?.kpis?.[this.selectedKpi] as any\n\n return html`\n <div class=\"overview-container\">\n <div class=\"overview-left\">\n <div class=\"overview-title\">KPI개요</div>\n <div class=\"overview-desc\">\n 건설현장 KPI관리 시스템은 프로젝트 성과를 측정하고, 모니터링하여 효율적인 공정 관리를 지원합니다. 주요 지표를\n 통해 현장 생산성, 품질, 안전 등을 실시간으로 파악할 수 있습니다.\n </div>\n </div>\n </div>\n <div class=\"group-tabs\">\n ${this.kpiCategories.map(\n (g: any, i: number) => html`\n <button\n class=\"group-tab\"\n ?selected=${i === this.selectedGroup}\n @click=${() => {\n this.selectedGroup = i\n this.selectedKpi = 0\n }}\n >\n ${g.name}\n </button>\n `\n )}\n </div>\n <!-- Y 섹션: 좌=Y 설명+산식 | 우=X-KPI 목록 -->\n <div class=\"main-content\">\n <div class=\"markdown\">\n <div class=\"kpi-title\">${group.name}</div>\n ${group.description ? html`<div style=\"line-height:1.7;margin-bottom:12px;\">${group.description}</div>` : ''}\n ${group.formula\n ? html`\n <div class=\"subtitle\"><span class=\"subtitle-icon\"></span> 산식</div>\n <div class=\"kpi-formula\">${group.formula}</div>\n `\n : ''}\n </div>\n <div class=\"toc\">\n <div class=\"submenu-header\">\n <span class=\"submenu-icon\"></span>\n <span class=\"toc-title\">${selctedY.name} 지표</span>\n </div>\n <ul class=\"toc-list\">\n ${group?.kpis?.map(\n (k: any, i: number) => html`\n <li>\n <button class=\"toc-btn\" ?selected=${i === this.selectedKpi} @click=${() => (this.selectedKpi = i)}>\n ${k.name}\n </button>\n </li>\n `\n )}\n </ul>\n </div>\n </div>\n\n <!-- X 섹션: 좌=X 설명+산식 | 우=성과분포/등급 -->\n <div class=\"x-section\">\n <div class=\"x-detail\">\n <div class=\"kpi-title\">${kpi?.name || ''}</div>\n <div style=\"line-height:1.7;margin-bottom:12px;\">${kpi?.description || ''}</div>\n ${kpi?.formula\n ? html`\n <div class=\"subtitle\"><span class=\"subtitle-icon\"></span> 주요산식</div>\n <div class=\"kpi-formula\">${kpi?.formula}</div>\n `\n : ''}\n ${this._renderTypeInfo(kpi)}\n </div>\n <div class=\"x-chart\">\n ${this._renderGradesContent(kpi)}\n </div>\n </div>\n `\n }\n\n /**\n * 성과분포/등급 콘텐츠 렌더링 — scoreType + valueType 기반 분기\n *\n * valueType=ASSESSED → 등급 기준 설명만 (감리자 직접 평가, 차트 없음)\n * scoreType=CUSTOM → 2D 히트맵 (공정률×편차율)\n * scoreType=LOOKUP → 성과 분포 커브 (value→score 매핑)\n * scoreType=DIRECT → 등급 데이터 없음 (Y/Z 등)\n */\n private _renderGradesContent(kpi: any) {\n if (!kpi?.grades) return html`<div style=\"color:#999;padding:20px;\">등급 데이터 없음</div>`\n\n const scoreType = kpi.scoreType\n const valueType = kpi.valueType\n const is2D = scoreType === 'CUSTOM' ||\n (kpi.grades && typeof kpi.grades === 'object' && !Array.isArray(kpi.grades) && kpi.grades.type === 'PROGRESS_DEVIATION_LOOKUP')\n const isArray = Array.isArray(kpi.grades) && kpi.grades.length > 0\n const isAssessment = valueType === 'ASSESSED' ||\n (!scoreType && !valueType && isArray && kpi.grades.length <= 5 &&\n kpi.grades.every((g: any) => g.score >= 1 && g.score <= 5 && g.score === Math.floor(g.score) && g.minValue === g.score))\n\n if (isAssessment) {\n return html`\n <div class=\"grades-title\">등급 기준</div>\n ${isArray ? this._renderAssessmentDescriptions(kpi.grades) : nothing}\n `\n }\n\n if (is2D) {\n return html`\n <div class=\"grades-title\">성과 분포</div>\n <div class=\"grades-chart\">\n <kpi-2d-lookup-chart .grades=${kpi.grades} .progressRate=${50}></kpi-2d-lookup-chart>\n </div>\n `\n }\n\n if (isArray) {\n return html`\n <div class=\"grades-title\">성과 분포</div>\n <div class=\"grades-chart\">\n <kpi-lookup-chart .grades=${kpi.grades} .showDots=${true} .scoreType=${kpi.scoreType || ''}></kpi-lookup-chart>\n </div>\n `\n }\n\n return html`<div style=\"color:#999;padding:20px;\">등급 데이터 없음</div>`\n }\n\n private _renderTypeInfo(kpi: any) {\n const valueLabels: Record<string, string> = {\n MEASURED: '외부 시스템 수집',\n ASSESSED: '감리자 직접 평가 (1~5)',\n CALCULATED: '산식 자동 계산',\n COMPOSITE: '다차원 복합 입력'\n }\n const scoreLabels: Record<string, string> = {\n DIRECT: 'value = score (변환 없음)',\n FORMULA: 'scoreFormula 수식 변환',\n LOOKUP: 'Grade Table 룩업',\n CUSTOM: '2D 룩업 (특수 변환)'\n }\n\n const vLabel = kpi?.valueType ? valueLabels[kpi.valueType] || kpi.valueType : null\n const sLabel = kpi?.scoreType ? scoreLabels[kpi.scoreType] || kpi.scoreType : null\n\n if (!vLabel && !sLabel) return nothing\n\n return html`\n <div class=\"type-info\">\n ${vLabel\n ? html`<span class=\"type-badge\"><span class=\"label\">Value 산정</span><span class=\"value\">${vLabel}</span></span>`\n : ''}\n ${sLabel\n ? html`<span class=\"type-badge\"><span class=\"label\">Score 산정</span><span class=\"value\">${sLabel}</span></span>`\n : ''}\n </div>\n `\n }\n\n private _renderAssessmentDescriptions(grades: any[]) {\n const colors = ['#e53935', '#ff9800', '#ffca28', '#66bb6a', '#2e7d32']\n const sorted = [...grades].sort((a, b) => a.score - b.score)\n\n return html`\n <div class=\"grade-descriptions\">\n ${sorted.map(\n (g: any) => html`\n <div class=\"grade-chip\">\n <span class=\"dot\" style=\"background: ${colors[(g.score || 1) - 1]}\"></span>\n <span class=\"score\">${g.score}점</span>\n <span class=\"desc\">${g.description || g.name || ''}</span>\n </div>\n `\n )}\n </div>\n `\n }\n}\n"]}
@@ -0,0 +1,20 @@
1
+ import '@material/web/button/filled-button.js';
2
+ import '@material/web/icon/icon.js';
3
+ import '@operato/data-grist/ox-grist.js';
4
+ import { LitElement } from 'lit';
5
+ export declare class KpiGrade2dEditor extends LitElement {
6
+ static styles: import("lit").CSSResult[];
7
+ kpi: any;
8
+ private rows;
9
+ private gristConfig;
10
+ private grist;
11
+ private svgEl;
12
+ firstUpdated(): void;
13
+ render(): import("lit-html").TemplateResult<1>;
14
+ private _fetchHandler;
15
+ private _onGristChange;
16
+ private _getCurrentRows;
17
+ private _drawChart;
18
+ private _validate;
19
+ private _save;
20
+ }
@@ -0,0 +1,445 @@
1
+ import { __decorate, __metadata } from "tslib";
2
+ import '@material/web/button/filled-button.js';
3
+ import '@material/web/icon/icon.js';
4
+ import '@operato/data-grist/ox-grist.js';
5
+ import gql from 'graphql-tag';
6
+ import * as d3 from 'd3';
7
+ import { LitElement, css, html } from 'lit';
8
+ import { customElement, property, state, query } from 'lit/decorators.js';
9
+ import { client } from '@operato/graphql';
10
+ import { notify } from '@operato/layout';
11
+ import { CommonHeaderStyles } from '@operato/styles';
12
+ import { DataGrist } from '@operato/data-grist';
13
+ // 점수 구간 정의 (높은 점수 = 좋음 = 낮은 공기편차)
14
+ const SCORE_ZONES = [
15
+ { score: 5, label: '5점', color: '#1b5e20', fillColor: 'rgba(76,175,80,0.15)' },
16
+ { score: 4, label: '4점', color: '#33691e', fillColor: 'rgba(139,195,74,0.15)' },
17
+ { score: 3, label: '3점', color: '#e65100', fillColor: 'rgba(255,193,7,0.15)' },
18
+ { score: 2, label: '2점', color: '#bf360c', fillColor: 'rgba(255,152,0,0.15)' },
19
+ { score: 1, label: '1점', color: '#b71c1c', fillColor: 'rgba(244,67,54,0.15)' }
20
+ ];
21
+ const BOUNDARIES = [
22
+ { key: 'boundary5to4', label: '5점 기준선', subLabel: '이하 → 5점', color: '#4caf50' },
23
+ { key: 'boundary4to3', label: '4점 기준선', subLabel: '이하 → 4점', color: '#8bc34a' },
24
+ { key: 'boundary3to2', label: '3점 기준선', subLabel: '이하 → 3점', color: '#ff9800' },
25
+ { key: 'boundary2to1', label: '2점 기준선', subLabel: '이하 → 2점', color: '#f44336' }
26
+ ];
27
+ let KpiGrade2dEditor = class KpiGrade2dEditor extends LitElement {
28
+ constructor() {
29
+ super(...arguments);
30
+ this.rows = [];
31
+ this.gristConfig = null;
32
+ }
33
+ firstUpdated() {
34
+ var _a;
35
+ const grades = (_a = this.kpi) === null || _a === void 0 ? void 0 : _a.grades;
36
+ this.rows = (grades === null || grades === void 0 ? void 0 : grades.rows) ? grades.rows.map((r) => (Object.assign({}, r))) : [];
37
+ this.gristConfig = {
38
+ list: { fields: ['progressRate', 'boundary5to4', 'boundary4to3', 'boundary3to2', 'boundary2to1'] },
39
+ columns: [
40
+ { type: 'gutter', gutterName: 'sequence', fixed: true },
41
+ {
42
+ type: 'number',
43
+ name: 'progressRate',
44
+ header: '공정률 (%)',
45
+ record: { editable: false },
46
+ sortable: false,
47
+ width: 90
48
+ },
49
+ {
50
+ type: 'number',
51
+ name: 'boundary5to4',
52
+ header: '5점 기준선',
53
+ record: { editable: true, align: 'right' },
54
+ width: 110
55
+ },
56
+ {
57
+ type: 'number',
58
+ name: 'boundary4to3',
59
+ header: '4점 기준선',
60
+ record: { editable: true, align: 'right' },
61
+ width: 110
62
+ },
63
+ {
64
+ type: 'number',
65
+ name: 'boundary3to2',
66
+ header: '3점 기준선',
67
+ record: { editable: true, align: 'right' },
68
+ width: 110
69
+ },
70
+ {
71
+ type: 'number',
72
+ name: 'boundary2to1',
73
+ header: '2점 기준선',
74
+ record: { editable: true, align: 'right' },
75
+ width: 110
76
+ }
77
+ ],
78
+ rows: { selectable: false, appendable: false },
79
+ pagination: { infinite: true }
80
+ };
81
+ requestAnimationFrame(() => this._drawChart(this.rows));
82
+ }
83
+ render() {
84
+ return html `
85
+ <div class="info-bar">
86
+ <span><strong>X축</strong>: 공정률 (%)</span>
87
+ <span><strong>Y축</strong>: 공기편차 (%)</span>
88
+ <span class="arrow">|</span>
89
+ <span class="rule">
90
+ 공기편차
91
+ <span class="score-badge" style="background:#4caf50">5</span>
92
+ 기준선 미만 → 5점,
93
+ <span class="score-badge" style="background:#8bc34a">4</span>
94
+ 기준선 미만 → 4점,
95
+ <span class="score-badge" style="background:#ff9800">3</span>
96
+ 기준선 미만 → 3점,
97
+ <span class="score-badge" style="background:#f44336">2</span>
98
+ 기준선 미만 → 2점, 초과 → 1점
99
+ </span>
100
+ </div>
101
+
102
+ <div class="chart-wrap">
103
+ <svg height="220"></svg>
104
+ </div>
105
+
106
+ <div class="legend">
107
+ ${BOUNDARIES.map(b => html `
108
+ <div class="legend-item">
109
+ <div class="legend-line" style="background:${b.color}"></div>
110
+ <span>${b.label}</span>
111
+ <span class="legend-sub">(${b.subLabel})</span>
112
+ </div>
113
+ `)}
114
+ </div>
115
+
116
+ <ox-grist
117
+ .mode=${'GRID'}
118
+ .config=${this.gristConfig}
119
+ .fetchHandler=${this._fetchHandler.bind(this)}
120
+ @change=${this._onGristChange}
121
+ ></ox-grist>
122
+
123
+ <div class="footer">
124
+ <md-filled-button @click=${this._save}>
125
+ <md-icon slot="icon">save</md-icon>
126
+ 저장
127
+ </md-filled-button>
128
+ </div>
129
+ `;
130
+ }
131
+ async _fetchHandler({ page, limit }) {
132
+ return { total: this.rows.length, records: this.rows };
133
+ }
134
+ _onGristChange() {
135
+ this._drawChart(this._getCurrentRows());
136
+ }
137
+ _getCurrentRows() {
138
+ var _a, _b;
139
+ const records = (_b = (_a = this.grist) === null || _a === void 0 ? void 0 : _a.dirtyData) === null || _b === void 0 ? void 0 : _b.records;
140
+ if (!records || records.length === 0)
141
+ return this.rows;
142
+ return records.map((r) => ({
143
+ progressRate: r.progressRate,
144
+ boundary5to4: Number(r.boundary5to4),
145
+ boundary4to3: Number(r.boundary4to3),
146
+ boundary3to2: Number(r.boundary3to2),
147
+ boundary2to1: Number(r.boundary2to1)
148
+ }));
149
+ }
150
+ _drawChart(rows) {
151
+ if (!this.svgEl || !rows.length)
152
+ return;
153
+ const W = this.svgEl.clientWidth || 560;
154
+ const H = 220;
155
+ const m = { top: 16, right: 32, bottom: 36, left: 56 };
156
+ const w = W - m.left - m.right;
157
+ const h = H - m.top - m.bottom;
158
+ const svg = d3.select(this.svgEl).attr('height', H);
159
+ svg.selectAll('*').remove();
160
+ const g = svg.append('g').attr('transform', `translate(${m.left},${m.top})`);
161
+ const x = d3.scaleLinear().domain([0, 99]).range([0, w]);
162
+ const allVals = rows.flatMap(r => [r.boundary5to4, r.boundary4to3, r.boundary3to2, r.boundary2to1]);
163
+ const [yMin, yMax] = d3.extent(allVals);
164
+ const pad = (yMax - yMin) * 0.15 || 5;
165
+ const y = d3.scaleLinear().domain([yMin - pad, yMax + pad]).range([h, 0]);
166
+ // 점수 구간 채우기 (5개 존)
167
+ const zoneDefs = [
168
+ { y0: _r => h, y1: r => y(r.boundary5to4), fill: 'rgba(76,175,80,0.12)', label: '5점' },
169
+ { y0: r => y(r.boundary5to4), y1: r => y(r.boundary4to3), fill: 'rgba(139,195,74,0.12)', label: '4점' },
170
+ { y0: r => y(r.boundary4to3), y1: r => y(r.boundary3to2), fill: 'rgba(255,193,7,0.12)', label: '3점' },
171
+ { y0: r => y(r.boundary3to2), y1: r => y(r.boundary2to1), fill: 'rgba(255,152,0,0.12)', label: '2점' },
172
+ { y0: r => y(r.boundary2to1), y1: _r => 0, fill: 'rgba(244,67,54,0.12)', label: '1점' }
173
+ ];
174
+ for (const zone of zoneDefs) {
175
+ const area = d3.area()
176
+ .x(r => x(r.progressRate))
177
+ .y0(zone.y0)
178
+ .y1(zone.y1)
179
+ .curve(d3.curveMonotoneX);
180
+ g.append('path')
181
+ .datum(rows)
182
+ .attr('fill', zone.fill)
183
+ .attr('d', area);
184
+ }
185
+ // 중간 공정률 지점에 점수 레이블
186
+ const midRow = rows[Math.floor(rows.length / 2)];
187
+ if (midRow) {
188
+ const scoreColors = ['#2e7d32', '#558b2f', '#f57f17', '#e65100', '#c62828'];
189
+ const scorePositions = [
190
+ (h + y(midRow.boundary5to4)) / 2,
191
+ (y(midRow.boundary5to4) + y(midRow.boundary4to3)) / 2,
192
+ (y(midRow.boundary4to3) + y(midRow.boundary3to2)) / 2,
193
+ (y(midRow.boundary3to2) + y(midRow.boundary2to1)) / 2,
194
+ y(midRow.boundary2to1) / 2
195
+ ];
196
+ const scoreLabels = ['5점', '4점', '3점', '2점', '1점'];
197
+ scorePositions.forEach((yPos, i) => {
198
+ if (yPos > 4 && yPos < h - 4) {
199
+ g.append('text')
200
+ .attr('x', w - 4)
201
+ .attr('y', yPos)
202
+ .attr('text-anchor', 'end')
203
+ .attr('dominant-baseline', 'middle')
204
+ .attr('font-size', '10px')
205
+ .attr('font-weight', 'bold')
206
+ .attr('fill', scoreColors[i])
207
+ .attr('opacity', 0.8)
208
+ .text(scoreLabels[i]);
209
+ }
210
+ });
211
+ }
212
+ // 격자선
213
+ g.append('g')
214
+ .attr('stroke', '#e0e0e0')
215
+ .attr('stroke-dasharray', '2,3')
216
+ .selectAll('line')
217
+ .data(y.ticks(5))
218
+ .join('line')
219
+ .attr('x1', 0).attr('x2', w)
220
+ .attr('y1', d => y(d)).attr('y2', d => y(d));
221
+ // y=0 기준선 (공기편차 0%)
222
+ const yZero = y(0);
223
+ if (yZero >= 0 && yZero <= h) {
224
+ g.append('line')
225
+ .attr('x1', 0).attr('x2', w)
226
+ .attr('y1', yZero).attr('y2', yZero)
227
+ .attr('stroke', '#90a4ae')
228
+ .attr('stroke-width', 1)
229
+ .attr('stroke-dasharray', '4,2');
230
+ g.append('text')
231
+ .attr('x', -4)
232
+ .attr('y', yZero)
233
+ .attr('text-anchor', 'end')
234
+ .attr('dominant-baseline', 'middle')
235
+ .attr('font-size', '9px')
236
+ .attr('fill', '#90a4ae')
237
+ .text('0%');
238
+ }
239
+ // X축
240
+ g.append('g')
241
+ .attr('transform', `translate(0,${h})`)
242
+ .call(d3.axisBottom(x).ticks(10).tickFormat(d => `${d}%`))
243
+ .selectAll('text')
244
+ .style('font-size', '10px');
245
+ // X축 레이블
246
+ g.append('text')
247
+ .attr('x', w / 2)
248
+ .attr('y', h + 30)
249
+ .attr('text-anchor', 'middle')
250
+ .attr('font-size', '11px')
251
+ .attr('fill', '#666')
252
+ .text('공정률 (%)');
253
+ // Y축
254
+ g.append('g')
255
+ .call(d3.axisLeft(y).ticks(5))
256
+ .selectAll('text')
257
+ .style('font-size', '10px');
258
+ // Y축 레이블
259
+ g.append('text')
260
+ .attr('transform', 'rotate(-90)')
261
+ .attr('x', -h / 2)
262
+ .attr('y', -44)
263
+ .attr('text-anchor', 'middle')
264
+ .attr('font-size', '11px')
265
+ .attr('fill', '#666')
266
+ .text('공기편차 (%)');
267
+ // 4개 경계선
268
+ for (const { key, color } of BOUNDARIES) {
269
+ const line = d3
270
+ .line()
271
+ .x(r => x(r.progressRate))
272
+ .y(r => y(r[key]))
273
+ .curve(d3.curveMonotoneX);
274
+ g.append('path')
275
+ .datum(rows)
276
+ .attr('fill', 'none')
277
+ .attr('stroke', color)
278
+ .attr('stroke-width', 2)
279
+ .attr('d', line);
280
+ }
281
+ }
282
+ _validate(rows) {
283
+ for (const row of rows) {
284
+ const { progressRate: p, boundary5to4: b54, boundary4to3: b43, boundary3to2: b32, boundary2to1: b21 } = row;
285
+ if (!(b54 < b43 && b43 < b32 && b32 < b21)) {
286
+ notify({
287
+ message: `공정률 ${p}%행: 기준선 값이 잘못됐습니다. 5점 < 4점 < 3점 < 2점 기준선 순서여야 합니다.`,
288
+ level: 'error'
289
+ });
290
+ return false;
291
+ }
292
+ }
293
+ return true;
294
+ }
295
+ async _save() {
296
+ const rows = this._getCurrentRows();
297
+ if (!this._validate(rows))
298
+ return;
299
+ const grades = { type: 'PROGRESS_DEVIATION_LOOKUP', rows };
300
+ try {
301
+ await client.mutate({
302
+ mutation: gql `
303
+ mutation ($id: String!, $patch: KpiPatch!) {
304
+ updateKpi(id: $id, patch: $patch) {
305
+ id
306
+ grades
307
+ scoreType
308
+ }
309
+ }
310
+ `,
311
+ variables: {
312
+ id: this.kpi.id,
313
+ patch: { grades, scoreType: 'CUSTOM' }
314
+ }
315
+ });
316
+ this.rows = rows;
317
+ this._drawChart(rows);
318
+ notify({ message: '저장되었습니다.' });
319
+ }
320
+ catch (e) {
321
+ notify({ message: '저장 중 오류가 발생했습니다.', level: 'error' });
322
+ }
323
+ }
324
+ };
325
+ KpiGrade2dEditor.styles = [
326
+ CommonHeaderStyles,
327
+ css `
328
+ :host {
329
+ display: flex;
330
+ flex-direction: column;
331
+ height: 100%;
332
+ background-color: var(--md-sys-color-surface);
333
+ }
334
+
335
+ .info-bar {
336
+ display: flex;
337
+ align-items: center;
338
+ gap: 24px;
339
+ padding: 8px 16px;
340
+ background: var(--md-sys-color-surface-variant, #f5f5f5);
341
+ border-bottom: 1px solid var(--md-sys-color-outline-variant, #ddd);
342
+ font-size: 12px;
343
+ color: var(--md-sys-color-on-surface-variant, #555);
344
+ flex-shrink: 0;
345
+ }
346
+
347
+ .info-bar .rule {
348
+ display: flex;
349
+ align-items: center;
350
+ gap: 6px;
351
+ }
352
+
353
+ .info-bar .arrow {
354
+ color: #888;
355
+ }
356
+
357
+ .score-badge {
358
+ display: inline-flex;
359
+ align-items: center;
360
+ justify-content: center;
361
+ width: 20px;
362
+ height: 20px;
363
+ border-radius: 50%;
364
+ font-size: 11px;
365
+ font-weight: bold;
366
+ color: #fff;
367
+ }
368
+
369
+ .chart-wrap {
370
+ padding: 8px 16px 0;
371
+ flex-shrink: 0;
372
+ position: relative;
373
+ }
374
+
375
+ .chart-wrap svg {
376
+ width: 100%;
377
+ display: block;
378
+ }
379
+
380
+ .legend {
381
+ display: flex;
382
+ gap: 20px;
383
+ padding: 4px 16px 8px;
384
+ font-size: 12px;
385
+ flex-shrink: 0;
386
+ flex-wrap: wrap;
387
+ }
388
+
389
+ .legend-item {
390
+ display: flex;
391
+ align-items: center;
392
+ gap: 6px;
393
+ }
394
+
395
+ .legend-line {
396
+ width: 20px;
397
+ height: 3px;
398
+ border-radius: 2px;
399
+ }
400
+
401
+ .legend-sub {
402
+ color: #888;
403
+ font-size: 11px;
404
+ }
405
+
406
+ ox-grist {
407
+ flex: 1;
408
+ overflow: hidden;
409
+ }
410
+
411
+ .footer {
412
+ display: flex;
413
+ justify-content: flex-end;
414
+ gap: 8px;
415
+ padding: 8px 16px;
416
+ border-top: 1px solid var(--md-sys-color-outline-variant, #ccc);
417
+ flex-shrink: 0;
418
+ }
419
+ `
420
+ ];
421
+ __decorate([
422
+ property({ type: Object }),
423
+ __metadata("design:type", Object)
424
+ ], KpiGrade2dEditor.prototype, "kpi", void 0);
425
+ __decorate([
426
+ state(),
427
+ __metadata("design:type", Array)
428
+ ], KpiGrade2dEditor.prototype, "rows", void 0);
429
+ __decorate([
430
+ state(),
431
+ __metadata("design:type", Object)
432
+ ], KpiGrade2dEditor.prototype, "gristConfig", void 0);
433
+ __decorate([
434
+ query('ox-grist'),
435
+ __metadata("design:type", DataGrist)
436
+ ], KpiGrade2dEditor.prototype, "grist", void 0);
437
+ __decorate([
438
+ query('svg'),
439
+ __metadata("design:type", SVGElement)
440
+ ], KpiGrade2dEditor.prototype, "svgEl", void 0);
441
+ KpiGrade2dEditor = __decorate([
442
+ customElement('kpi-grade-2d-editor')
443
+ ], KpiGrade2dEditor);
444
+ export { KpiGrade2dEditor };
445
+ //# sourceMappingURL=kpi-grade-2d-editor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kpi-grade-2d-editor.js","sourceRoot":"","sources":["../../../client/pages/kpi-admin/kpi-grade-2d-editor.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/button/filled-button.js'\nimport '@material/web/icon/icon.js'\nimport '@operato/data-grist/ox-grist.js'\n\nimport gql from 'graphql-tag'\nimport * as d3 from 'd3'\nimport { LitElement, css, html } from 'lit'\nimport { customElement, property, state, query } from 'lit/decorators.js'\nimport { client } from '@operato/graphql'\nimport { notify } from '@operato/layout'\nimport { CommonHeaderStyles } from '@operato/styles'\nimport { DataGrist, FetchOption } from '@operato/data-grist'\n\ninterface Grade2DRow {\n progressRate: number\n boundary5to4: number\n boundary4to3: number\n boundary3to2: number\n boundary2to1: number\n}\n\n// 점수 구간 정의 (높은 점수 = 좋음 = 낮은 공기편차)\nconst SCORE_ZONES = [\n { score: 5, label: '5점', color: '#1b5e20', fillColor: 'rgba(76,175,80,0.15)' },\n { score: 4, label: '4점', color: '#33691e', fillColor: 'rgba(139,195,74,0.15)' },\n { score: 3, label: '3점', color: '#e65100', fillColor: 'rgba(255,193,7,0.15)' },\n { score: 2, label: '2점', color: '#bf360c', fillColor: 'rgba(255,152,0,0.15)' },\n { score: 1, label: '1점', color: '#b71c1c', fillColor: 'rgba(244,67,54,0.15)' }\n]\n\nconst BOUNDARIES = [\n { key: 'boundary5to4' as const, label: '5점 기준선', subLabel: '이하 → 5점', color: '#4caf50' },\n { key: 'boundary4to3' as const, label: '4점 기준선', subLabel: '이하 → 4점', color: '#8bc34a' },\n { key: 'boundary3to2' as const, label: '3점 기준선', subLabel: '이하 → 3점', color: '#ff9800' },\n { key: 'boundary2to1' as const, label: '2점 기준선', subLabel: '이하 → 2점', color: '#f44336' }\n]\n\n@customElement('kpi-grade-2d-editor')\nexport class KpiGrade2dEditor extends LitElement {\n static styles = [\n CommonHeaderStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n height: 100%;\n background-color: var(--md-sys-color-surface);\n }\n\n .info-bar {\n display: flex;\n align-items: center;\n gap: 24px;\n padding: 8px 16px;\n background: var(--md-sys-color-surface-variant, #f5f5f5);\n border-bottom: 1px solid var(--md-sys-color-outline-variant, #ddd);\n font-size: 12px;\n color: var(--md-sys-color-on-surface-variant, #555);\n flex-shrink: 0;\n }\n\n .info-bar .rule {\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .info-bar .arrow {\n color: #888;\n }\n\n .score-badge {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 20px;\n border-radius: 50%;\n font-size: 11px;\n font-weight: bold;\n color: #fff;\n }\n\n .chart-wrap {\n padding: 8px 16px 0;\n flex-shrink: 0;\n position: relative;\n }\n\n .chart-wrap svg {\n width: 100%;\n display: block;\n }\n\n .legend {\n display: flex;\n gap: 20px;\n padding: 4px 16px 8px;\n font-size: 12px;\n flex-shrink: 0;\n flex-wrap: wrap;\n }\n\n .legend-item {\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .legend-line {\n width: 20px;\n height: 3px;\n border-radius: 2px;\n }\n\n .legend-sub {\n color: #888;\n font-size: 11px;\n }\n\n ox-grist {\n flex: 1;\n overflow: hidden;\n }\n\n .footer {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n padding: 8px 16px;\n border-top: 1px solid var(--md-sys-color-outline-variant, #ccc);\n flex-shrink: 0;\n }\n `\n ]\n\n @property({ type: Object }) kpi: any\n @state() private rows: Grade2DRow[] = []\n @state() private gristConfig: any = null\n @query('ox-grist') private grist!: DataGrist\n @query('svg') private svgEl!: SVGElement\n\n firstUpdated() {\n const grades = this.kpi?.grades\n this.rows = grades?.rows ? grades.rows.map((r: any) => ({ ...r })) : []\n\n this.gristConfig = {\n list: { fields: ['progressRate', 'boundary5to4', 'boundary4to3', 'boundary3to2', 'boundary2to1'] },\n columns: [\n { type: 'gutter', gutterName: 'sequence', fixed: true },\n {\n type: 'number',\n name: 'progressRate',\n header: '공정률 (%)',\n record: { editable: false },\n sortable: false,\n width: 90\n },\n {\n type: 'number',\n name: 'boundary5to4',\n header: '5점 기준선',\n record: { editable: true, align: 'right' },\n width: 110\n },\n {\n type: 'number',\n name: 'boundary4to3',\n header: '4점 기준선',\n record: { editable: true, align: 'right' },\n width: 110\n },\n {\n type: 'number',\n name: 'boundary3to2',\n header: '3점 기준선',\n record: { editable: true, align: 'right' },\n width: 110\n },\n {\n type: 'number',\n name: 'boundary2to1',\n header: '2점 기준선',\n record: { editable: true, align: 'right' },\n width: 110\n }\n ],\n rows: { selectable: false, appendable: false },\n pagination: { infinite: true }\n }\n\n requestAnimationFrame(() => this._drawChart(this.rows))\n }\n\n render() {\n return html`\n <div class=\"info-bar\">\n <span><strong>X축</strong>: 공정률 (%)</span>\n <span><strong>Y축</strong>: 공기편차 (%)</span>\n <span class=\"arrow\">|</span>\n <span class=\"rule\">\n 공기편차\n <span class=\"score-badge\" style=\"background:#4caf50\">5</span>\n 기준선 미만 → 5점,\n <span class=\"score-badge\" style=\"background:#8bc34a\">4</span>\n 기준선 미만 → 4점,\n <span class=\"score-badge\" style=\"background:#ff9800\">3</span>\n 기준선 미만 → 3점,\n <span class=\"score-badge\" style=\"background:#f44336\">2</span>\n 기준선 미만 → 2점, 초과 → 1점\n </span>\n </div>\n\n <div class=\"chart-wrap\">\n <svg height=\"220\"></svg>\n </div>\n\n <div class=\"legend\">\n ${BOUNDARIES.map(\n b => html`\n <div class=\"legend-item\">\n <div class=\"legend-line\" style=\"background:${b.color}\"></div>\n <span>${b.label}</span>\n <span class=\"legend-sub\">(${b.subLabel})</span>\n </div>\n `\n )}\n </div>\n\n <ox-grist\n .mode=${'GRID'}\n .config=${this.gristConfig}\n .fetchHandler=${this._fetchHandler.bind(this)}\n @change=${this._onGristChange}\n ></ox-grist>\n\n <div class=\"footer\">\n <md-filled-button @click=${this._save}>\n <md-icon slot=\"icon\">save</md-icon>\n 저장\n </md-filled-button>\n </div>\n `\n }\n\n private async _fetchHandler({ page, limit }: FetchOption) {\n return { total: this.rows.length, records: this.rows }\n }\n\n private _onGristChange() {\n this._drawChart(this._getCurrentRows())\n }\n\n private _getCurrentRows(): Grade2DRow[] {\n const records = this.grist?.dirtyData?.records\n if (!records || records.length === 0) return this.rows\n\n return records.map((r: any) => ({\n progressRate: r.progressRate,\n boundary5to4: Number(r.boundary5to4),\n boundary4to3: Number(r.boundary4to3),\n boundary3to2: Number(r.boundary3to2),\n boundary2to1: Number(r.boundary2to1)\n }))\n }\n\n private _drawChart(rows: Grade2DRow[]) {\n if (!this.svgEl || !rows.length) return\n\n const W = this.svgEl.clientWidth || 560\n const H = 220\n const m = { top: 16, right: 32, bottom: 36, left: 56 }\n const w = W - m.left - m.right\n const h = H - m.top - m.bottom\n\n const svg = d3.select(this.svgEl).attr('height', H)\n svg.selectAll('*').remove()\n\n const g = svg.append('g').attr('transform', `translate(${m.left},${m.top})`)\n\n const x = d3.scaleLinear().domain([0, 99]).range([0, w])\n\n const allVals = rows.flatMap(r => [r.boundary5to4, r.boundary4to3, r.boundary3to2, r.boundary2to1])\n const [yMin, yMax] = d3.extent(allVals) as [number, number]\n const pad = (yMax - yMin) * 0.15 || 5\n const y = d3.scaleLinear().domain([yMin - pad, yMax + pad]).range([h, 0])\n\n // 점수 구간 채우기 (5개 존)\n const zoneDefs: Array<{ y0: (r: Grade2DRow) => number; y1: (r: Grade2DRow) => number; fill: string; label: string }> = [\n { y0: _r => h, y1: r => y(r.boundary5to4), fill: 'rgba(76,175,80,0.12)', label: '5점' },\n { y0: r => y(r.boundary5to4), y1: r => y(r.boundary4to3), fill: 'rgba(139,195,74,0.12)', label: '4점' },\n { y0: r => y(r.boundary4to3), y1: r => y(r.boundary3to2), fill: 'rgba(255,193,7,0.12)', label: '3점' },\n { y0: r => y(r.boundary3to2), y1: r => y(r.boundary2to1), fill: 'rgba(255,152,0,0.12)', label: '2점' },\n { y0: r => y(r.boundary2to1), y1: _r => 0, fill: 'rgba(244,67,54,0.12)', label: '1점' }\n ]\n\n for (const zone of zoneDefs) {\n const area = d3.area<Grade2DRow>()\n .x(r => x(r.progressRate))\n .y0(zone.y0)\n .y1(zone.y1)\n .curve(d3.curveMonotoneX)\n\n g.append('path')\n .datum(rows)\n .attr('fill', zone.fill)\n .attr('d', area)\n }\n\n // 중간 공정률 지점에 점수 레이블\n const midRow = rows[Math.floor(rows.length / 2)]\n if (midRow) {\n const scoreColors = ['#2e7d32', '#558b2f', '#f57f17', '#e65100', '#c62828']\n const scorePositions = [\n (h + y(midRow.boundary5to4)) / 2,\n (y(midRow.boundary5to4) + y(midRow.boundary4to3)) / 2,\n (y(midRow.boundary4to3) + y(midRow.boundary3to2)) / 2,\n (y(midRow.boundary3to2) + y(midRow.boundary2to1)) / 2,\n y(midRow.boundary2to1) / 2\n ]\n const scoreLabels = ['5점', '4점', '3점', '2점', '1점']\n\n scorePositions.forEach((yPos, i) => {\n if (yPos > 4 && yPos < h - 4) {\n g.append('text')\n .attr('x', w - 4)\n .attr('y', yPos)\n .attr('text-anchor', 'end')\n .attr('dominant-baseline', 'middle')\n .attr('font-size', '10px')\n .attr('font-weight', 'bold')\n .attr('fill', scoreColors[i])\n .attr('opacity', 0.8)\n .text(scoreLabels[i])\n }\n })\n }\n\n // 격자선\n g.append('g')\n .attr('stroke', '#e0e0e0')\n .attr('stroke-dasharray', '2,3')\n .selectAll('line')\n .data(y.ticks(5))\n .join('line')\n .attr('x1', 0).attr('x2', w)\n .attr('y1', d => y(d)).attr('y2', d => y(d))\n\n // y=0 기준선 (공기편차 0%)\n const yZero = y(0)\n if (yZero >= 0 && yZero <= h) {\n g.append('line')\n .attr('x1', 0).attr('x2', w)\n .attr('y1', yZero).attr('y2', yZero)\n .attr('stroke', '#90a4ae')\n .attr('stroke-width', 1)\n .attr('stroke-dasharray', '4,2')\n g.append('text')\n .attr('x', -4)\n .attr('y', yZero)\n .attr('text-anchor', 'end')\n .attr('dominant-baseline', 'middle')\n .attr('font-size', '9px')\n .attr('fill', '#90a4ae')\n .text('0%')\n }\n\n // X축\n g.append('g')\n .attr('transform', `translate(0,${h})`)\n .call(d3.axisBottom(x).ticks(10).tickFormat(d => `${d}%`))\n .selectAll('text')\n .style('font-size', '10px')\n\n // X축 레이블\n g.append('text')\n .attr('x', w / 2)\n .attr('y', h + 30)\n .attr('text-anchor', 'middle')\n .attr('font-size', '11px')\n .attr('fill', '#666')\n .text('공정률 (%)')\n\n // Y축\n g.append('g')\n .call(d3.axisLeft(y).ticks(5))\n .selectAll('text')\n .style('font-size', '10px')\n\n // Y축 레이블\n g.append('text')\n .attr('transform', 'rotate(-90)')\n .attr('x', -h / 2)\n .attr('y', -44)\n .attr('text-anchor', 'middle')\n .attr('font-size', '11px')\n .attr('fill', '#666')\n .text('공기편차 (%)')\n\n // 4개 경계선\n for (const { key, color } of BOUNDARIES) {\n const line = d3\n .line<Grade2DRow>()\n .x(r => x(r.progressRate))\n .y(r => y(r[key]))\n .curve(d3.curveMonotoneX)\n\n g.append('path')\n .datum(rows)\n .attr('fill', 'none')\n .attr('stroke', color)\n .attr('stroke-width', 2)\n .attr('d', line)\n }\n }\n\n private _validate(rows: Grade2DRow[]): boolean {\n for (const row of rows) {\n const { progressRate: p, boundary5to4: b54, boundary4to3: b43, boundary3to2: b32, boundary2to1: b21 } = row\n if (!(b54 < b43 && b43 < b32 && b32 < b21)) {\n notify({\n message: `공정률 ${p}%행: 기준선 값이 잘못됐습니다. 5점 < 4점 < 3점 < 2점 기준선 순서여야 합니다.`,\n level: 'error'\n })\n return false\n }\n }\n return true\n }\n\n private async _save() {\n const rows = this._getCurrentRows()\n if (!this._validate(rows)) return\n\n const grades = { type: 'PROGRESS_DEVIATION_LOOKUP', rows }\n\n try {\n await client.mutate({\n mutation: gql`\n mutation ($id: String!, $patch: KpiPatch!) {\n updateKpi(id: $id, patch: $patch) {\n id\n grades\n scoreType\n }\n }\n `,\n variables: {\n id: this.kpi.id,\n patch: { grades, scoreType: 'CUSTOM' }\n }\n })\n\n this.rows = rows\n this._drawChart(rows)\n notify({ message: '저장되었습니다.' })\n } catch (e) {\n notify({ message: '저장 중 오류가 발생했습니다.', level: 'error' })\n }\n }\n}\n"]}
@@ -0,0 +1,18 @@
1
+ import { PageView } from '@operato/shell';
2
+ export declare class KpiSystemGuide extends PageView {
3
+ static styles: import("lit").CSSResult[];
4
+ kpiRoot: any[];
5
+ metrics: any[];
6
+ loading: boolean;
7
+ selectedDetail: any;
8
+ get context(): {
9
+ title: string;
10
+ };
11
+ firstUpdated(): Promise<void>;
12
+ /** 메트릭이 사용되는 KPI 목록 */
13
+ private getMetricUsage;
14
+ private showDetail;
15
+ private closeDetail;
16
+ render(): import("lit-html").TemplateResult<1>;
17
+ private _renderDetailPopup;
18
+ }