@dssp/dkpi 1.0.0-alpha.99 → 1.0.0

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 (236) hide show
  1. package/package.json +55 -55
  2. package/dist-client/bootstrap.d.ts +0 -9
  3. package/dist-client/bootstrap.js +0 -272
  4. package/dist-client/bootstrap.js.map +0 -1
  5. package/dist-client/components/kpi-2d-lookup-chart.d.ts +0 -50
  6. package/dist-client/components/kpi-2d-lookup-chart.js +0 -263
  7. package/dist-client/components/kpi-2d-lookup-chart.js.map +0 -1
  8. package/dist-client/components/kpi-boxplot-chart.d.ts +0 -31
  9. package/dist-client/components/kpi-boxplot-chart.js +0 -306
  10. package/dist-client/components/kpi-boxplot-chart.js.map +0 -1
  11. package/dist-client/components/kpi-lookup-chart.d.ts +0 -58
  12. package/dist-client/components/kpi-lookup-chart.js +0 -464
  13. package/dist-client/components/kpi-lookup-chart.js.map +0 -1
  14. package/dist-client/components/kpi-mini-trend-chart.d.ts +0 -14
  15. package/dist-client/components/kpi-mini-trend-chart.js +0 -180
  16. package/dist-client/components/kpi-mini-trend-chart.js.map +0 -1
  17. package/dist-client/components/kpi-radar-chart.d.ts +0 -17
  18. package/dist-client/components/kpi-radar-chart.js +0 -259
  19. package/dist-client/components/kpi-radar-chart.js.map +0 -1
  20. package/dist-client/components/kpi-single-boxplot-chart.d.ts +0 -36
  21. package/dist-client/components/kpi-single-boxplot-chart.js +0 -417
  22. package/dist-client/components/kpi-single-boxplot-chart.js.map +0 -1
  23. package/dist-client/components/kpi-step-lookup-chart.d.ts +0 -33
  24. package/dist-client/components/kpi-step-lookup-chart.js +0 -181
  25. package/dist-client/components/kpi-step-lookup-chart.js.map +0 -1
  26. package/dist-client/components/kpi-trend-chart.d.ts +0 -25
  27. package/dist-client/components/kpi-trend-chart.js +0 -241
  28. package/dist-client/components/kpi-trend-chart.js.map +0 -1
  29. package/dist-client/components/sv-pagenation-control.d.ts +0 -18
  30. package/dist-client/components/sv-pagenation-control.js +0 -142
  31. package/dist-client/components/sv-pagenation-control.js.map +0 -1
  32. package/dist-client/entries/auth/checkin.d.ts +0 -38
  33. package/dist-client/entries/auth/checkin.js +0 -551
  34. package/dist-client/entries/auth/checkin.js.map +0 -1
  35. package/dist-client/entries/public/home.d.ts +0 -8
  36. package/dist-client/entries/public/home.js +0 -97
  37. package/dist-client/entries/public/home.js.map +0 -1
  38. package/dist-client/google-map/common-google-map.d.ts +0 -35
  39. package/dist-client/google-map/common-google-map.js +0 -349
  40. package/dist-client/google-map/common-google-map.js.map +0 -1
  41. package/dist-client/google-map/google-map-loader.d.ts +0 -6
  42. package/dist-client/google-map/google-map-loader.js +0 -23
  43. package/dist-client/google-map/google-map-loader.js.map +0 -1
  44. package/dist-client/icons/menu-icons.d.ts +0 -11
  45. package/dist-client/icons/menu-icons.js +0 -126
  46. package/dist-client/icons/menu-icons.js.map +0 -1
  47. package/dist-client/index.d.ts +0 -0
  48. package/dist-client/index.js +0 -2
  49. package/dist-client/index.js.map +0 -1
  50. package/dist-client/menu.d.ts +0 -23
  51. package/dist-client/menu.js +0 -59
  52. package/dist-client/menu.js.map +0 -1
  53. package/dist-client/pages/component/project-update-header.d.ts +0 -1
  54. package/dist-client/pages/component/project-update-header.js +0 -127
  55. package/dist-client/pages/component/project-update-header.js.map +0 -1
  56. package/dist-client/pages/kpi-admin/dssp-kpi-list-page.d.ts +0 -22
  57. package/dist-client/pages/kpi-admin/dssp-kpi-list-page.js +0 -57
  58. package/dist-client/pages/kpi-admin/dssp-kpi-list-page.js.map +0 -1
  59. package/dist-client/pages/kpi-admin/dssp-kpi-overview.d.ts +0 -47
  60. package/dist-client/pages/kpi-admin/dssp-kpi-overview.js +0 -393
  61. package/dist-client/pages/kpi-admin/dssp-kpi-overview.js.map +0 -1
  62. package/dist-client/pages/kpi-admin/kpi-grade-2d-editor.d.ts +0 -20
  63. package/dist-client/pages/kpi-admin/kpi-grade-2d-editor.js +0 -445
  64. package/dist-client/pages/kpi-admin/kpi-grade-2d-editor.js.map +0 -1
  65. package/dist-client/pages/kpi-admin/kpi-system-guide.d.ts +0 -18
  66. package/dist-client/pages/kpi-admin/kpi-system-guide.js +0 -696
  67. package/dist-client/pages/kpi-admin/kpi-system-guide.js.map +0 -1
  68. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.d.ts +0 -18
  69. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js +0 -259
  70. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js.map +0 -1
  71. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.d.ts +0 -22
  72. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js +0 -346
  73. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js.map +0 -1
  74. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.d.ts +0 -26
  75. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js +0 -433
  76. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js.map +0 -1
  77. package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.d.ts +0 -8
  78. package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.js +0 -78
  79. package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.js.map +0 -1
  80. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.d.ts +0 -46
  81. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js +0 -945
  82. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js.map +0 -1
  83. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.d.ts +0 -34
  84. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js +0 -238
  85. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js.map +0 -1
  86. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.d.ts +0 -37
  87. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js +0 -689
  88. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js.map +0 -1
  89. package/dist-client/pages/kpi-dashboard/kpi-alert-panel.d.ts +0 -18
  90. package/dist-client/pages/kpi-dashboard/kpi-alert-panel.js +0 -131
  91. package/dist-client/pages/kpi-dashboard/kpi-alert-panel.js.map +0 -1
  92. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.d.ts +0 -57
  93. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js +0 -866
  94. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js.map +0 -1
  95. package/dist-client/pages/kpi-dashboard/kpi-dashboard.d.ts +0 -63
  96. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js +0 -1097
  97. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js.map +0 -1
  98. package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.d.ts +0 -12
  99. package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.js +0 -82
  100. package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.js.map +0 -1
  101. package/dist-client/pages/kpi-dashboard/kpi-history-viewer.d.ts +0 -11
  102. package/dist-client/pages/kpi-dashboard/kpi-history-viewer.js +0 -65
  103. package/dist-client/pages/kpi-dashboard/kpi-history-viewer.js.map +0 -1
  104. package/dist-client/pages/kpi-dashboard/kpi-list-summary.d.ts +0 -13
  105. package/dist-client/pages/kpi-dashboard/kpi-list-summary.js +0 -115
  106. package/dist-client/pages/kpi-dashboard/kpi-list-summary.js.map +0 -1
  107. package/dist-client/pages/kpi-dashboard/kpi-performance-summary.d.ts +0 -15
  108. package/dist-client/pages/kpi-dashboard/kpi-performance-summary.js +0 -147
  109. package/dist-client/pages/kpi-dashboard/kpi-performance-summary.js.map +0 -1
  110. package/dist-client/pages/kpi-dashboard/kpi-value-entry.d.ts +0 -7
  111. package/dist-client/pages/kpi-dashboard/kpi-value-entry.js +0 -86
  112. package/dist-client/pages/kpi-dashboard/kpi-value-entry.js.map +0 -1
  113. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.d.ts +0 -57
  114. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js +0 -719
  115. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js.map +0 -1
  116. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.d.ts +0 -23
  117. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js +0 -76
  118. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js.map +0 -1
  119. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.d.ts +0 -68
  120. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js +0 -394
  121. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js.map +0 -1
  122. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.d.ts +0 -12
  123. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js +0 -174
  124. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js.map +0 -1
  125. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.d.ts +0 -40
  126. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js +0 -190
  127. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js.map +0 -1
  128. package/dist-client/pages/kpi-value/kpi-value-importer.d.ts +0 -23
  129. package/dist-client/pages/kpi-value/kpi-value-importer.js +0 -93
  130. package/dist-client/pages/kpi-value/kpi-value-importer.js.map +0 -1
  131. package/dist-client/pages/kpi-value/kpi-value-list-page.d.ts +0 -71
  132. package/dist-client/pages/kpi-value/kpi-value-list-page.js +0 -454
  133. package/dist-client/pages/kpi-value/kpi-value-list-page.js.map +0 -1
  134. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.d.ts +0 -69
  135. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js +0 -790
  136. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js.map +0 -1
  137. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.d.ts +0 -22
  138. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js +0 -344
  139. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js.map +0 -1
  140. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.d.ts +0 -21
  141. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.js +0 -335
  142. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.js.map +0 -1
  143. package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.d.ts +0 -51
  144. package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.js +0 -674
  145. package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.js.map +0 -1
  146. package/dist-client/pages/sv-project-complete.d.ts +0 -31
  147. package/dist-client/pages/sv-project-complete.js +0 -359
  148. package/dist-client/pages/sv-project-complete.js.map +0 -1
  149. package/dist-client/pages/sv-project-completed-list.d.ts +0 -27
  150. package/dist-client/pages/sv-project-completed-list.js +0 -416
  151. package/dist-client/pages/sv-project-completed-list.js.map +0 -1
  152. package/dist-client/pages/sv-project-detail.d.ts +0 -73
  153. package/dist-client/pages/sv-project-detail.js +0 -1501
  154. package/dist-client/pages/sv-project-detail.js.map +0 -1
  155. package/dist-client/pages/sv-project-list.d.ts +0 -175
  156. package/dist-client/pages/sv-project-list.js +0 -581
  157. package/dist-client/pages/sv-project-list.js.map +0 -1
  158. package/dist-client/pages/sv-project-update.d.ts +0 -91
  159. package/dist-client/pages/sv-project-update.js +0 -1150
  160. package/dist-client/pages/sv-project-update.js.map +0 -1
  161. package/dist-client/pages/sv-user-management.d.ts +0 -6
  162. package/dist-client/pages/sv-user-management.js +0 -224
  163. package/dist-client/pages/sv-user-management.js.map +0 -1
  164. package/dist-client/route.d.ts +0 -1
  165. package/dist-client/route.js +0 -58
  166. package/dist-client/route.js.map +0 -1
  167. package/dist-client/shared/complete-api.d.ts +0 -45
  168. package/dist-client/shared/complete-api.js +0 -272
  169. package/dist-client/shared/complete-api.js.map +0 -1
  170. package/dist-client/shared/func.d.ts +0 -2
  171. package/dist-client/shared/func.js +0 -22
  172. package/dist-client/shared/func.js.map +0 -1
  173. package/dist-client/shared/geo-group-mapping.d.ts +0 -25
  174. package/dist-client/shared/geo-group-mapping.js +0 -189
  175. package/dist-client/shared/geo-group-mapping.js.map +0 -1
  176. package/dist-client/shared/integration-fetch.d.ts +0 -35
  177. package/dist-client/shared/integration-fetch.js +0 -53
  178. package/dist-client/shared/integration-fetch.js.map +0 -1
  179. package/dist-client/shared/kpi-project-sync.d.ts +0 -16
  180. package/dist-client/shared/kpi-project-sync.js +0 -129
  181. package/dist-client/shared/kpi-project-sync.js.map +0 -1
  182. package/dist-client/themes/dark.css +0 -51
  183. package/dist-client/themes/light.css +0 -51
  184. package/dist-client/tsconfig.tsbuildinfo +0 -1
  185. package/dist-client/viewparts/menu-tools.d.ts +0 -62
  186. package/dist-client/viewparts/menu-tools.js +0 -554
  187. package/dist-client/viewparts/menu-tools.js.map +0 -1
  188. package/dist-client/viewparts/user-circle.d.ts +0 -5
  189. package/dist-client/viewparts/user-circle.js +0 -26
  190. package/dist-client/viewparts/user-circle.js.map +0 -1
  191. package/dist-server/index.d.ts +0 -2
  192. package/dist-server/index.js +0 -6
  193. package/dist-server/index.js.map +0 -1
  194. package/dist-server/migrations/index.d.ts +0 -1
  195. package/dist-server/migrations/index.js +0 -12
  196. package/dist-server/migrations/index.js.map +0 -1
  197. package/dist-server/scripts/calculate-kpi-scores.d.ts +0 -10
  198. package/dist-server/scripts/calculate-kpi-scores.js +0 -333
  199. package/dist-server/scripts/calculate-kpi-scores.js.map +0 -1
  200. package/dist-server/scripts/load-grade-data-migration.d.ts +0 -14
  201. package/dist-server/scripts/load-grade-data-migration.js +0 -279
  202. package/dist-server/scripts/load-grade-data-migration.js.map +0 -1
  203. package/dist-server/scripts/propagate-parent-kpi-values.d.ts +0 -14
  204. package/dist-server/scripts/propagate-parent-kpi-values.js +0 -786
  205. package/dist-server/scripts/propagate-parent-kpi-values.js.map +0 -1
  206. package/dist-server/scripts/recalculate-by-project-name.d.ts +0 -2
  207. package/dist-server/scripts/recalculate-by-project-name.js +0 -72
  208. package/dist-server/scripts/recalculate-by-project-name.js.map +0 -1
  209. package/dist-server/service/index.d.ts +0 -4
  210. package/dist-server/service/index.js +0 -20
  211. package/dist-server/service/index.js.map +0 -1
  212. package/dist-server/service/kpi-metric-value/index.d.ts +0 -4
  213. package/dist-server/service/kpi-metric-value/index.js +0 -8
  214. package/dist-server/service/kpi-metric-value/index.js.map +0 -1
  215. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.d.ts +0 -171
  216. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js +0 -993
  217. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js.map +0 -1
  218. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.d.ts +0 -12
  219. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js +0 -56
  220. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js.map +0 -1
  221. package/dist-server/service/kpi-stat/index.d.ts +0 -4
  222. package/dist-server/service/kpi-stat/index.js +0 -8
  223. package/dist-server/service/kpi-stat/index.js.map +0 -1
  224. package/dist-server/service/kpi-stat/kpi-stat-query.d.ts +0 -13
  225. package/dist-server/service/kpi-stat/kpi-stat-query.js +0 -751
  226. package/dist-server/service/kpi-stat/kpi-stat-query.js.map +0 -1
  227. package/dist-server/service/kpi-stat/kpi-stat-types.d.ts +0 -32
  228. package/dist-server/service/kpi-stat/kpi-stat-types.js +0 -180
  229. package/dist-server/service/kpi-stat/kpi-stat-types.js.map +0 -1
  230. package/dist-server/service/kpi-value/index.d.ts +0 -3
  231. package/dist-server/service/kpi-value/index.js +0 -7
  232. package/dist-server/service/kpi-value/index.js.map +0 -1
  233. package/dist-server/service/kpi-value/kpi-value-query.d.ts +0 -8
  234. package/dist-server/service/kpi-value/kpi-value-query.js +0 -90
  235. package/dist-server/service/kpi-value/kpi-value-query.js.map +0 -1
  236. package/dist-server/tsconfig.tsbuildinfo +0 -1
@@ -1,445 +0,0 @@
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
@@ -1 +0,0 @@
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"]}
@@ -1,18 +0,0 @@
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 목록 — 비활성 KPI/Y 그룹 제외 */
13
- private getMetricUsage;
14
- private showDetail;
15
- private closeDetail;
16
- render(): import("lit-html").TemplateResult<1>;
17
- private _renderDetailPopup;
18
- }