@dssp/dkpi 1.0.0-alpha.5 → 1.0.0-alpha.51

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 (134) hide show
  1. package/assets/favicon.ico +0 -0
  2. package/assets/manifest/apple-1024.png +0 -0
  3. package/assets/manifest/apple-120.png +0 -0
  4. package/assets/manifest/apple-152.png +0 -0
  5. package/assets/manifest/apple-167.png +0 -0
  6. package/assets/manifest/apple-180.png +0 -0
  7. package/assets/manifest/apple-touch-icon.png +0 -0
  8. package/assets/manifest/badge-128x128.png +0 -0
  9. package/assets/manifest/chrome-splashscreen-icon-384x384.png +0 -0
  10. package/assets/manifest/chrome-touch-icon-192x192.png +0 -0
  11. package/assets/manifest/icon-128x128.png +0 -0
  12. package/assets/manifest/icon-192x192.png +0 -0
  13. package/assets/manifest/icon-512x512.png +0 -0
  14. package/assets/manifest/icon-72x72.png +0 -0
  15. package/assets/manifest/icon-96x96.png +0 -0
  16. package/assets/manifest/image-metaog.png +0 -0
  17. package/assets/manifest/maskable_icon.png +0 -0
  18. package/assets/manifest/ms-icon-144x144.png +0 -0
  19. package/assets/manifest/ms-touch-icon-144x144-precomposed.png +0 -0
  20. package/assets/videos/intro.mp4 +0 -0
  21. package/dist-client/bootstrap.js +64 -4
  22. package/dist-client/bootstrap.js.map +1 -1
  23. package/dist-client/components/kpi-boxplot-chart.d.ts +24 -0
  24. package/dist-client/components/kpi-boxplot-chart.js +328 -0
  25. package/dist-client/components/kpi-boxplot-chart.js.map +1 -0
  26. package/dist-client/components/kpi-radar-chart.d.ts +16 -0
  27. package/dist-client/components/kpi-radar-chart.js +139 -0
  28. package/dist-client/components/kpi-radar-chart.js.map +1 -0
  29. package/dist-client/components/kpi-single-boxplot-chart.d.ts +24 -0
  30. package/dist-client/components/kpi-single-boxplot-chart.js +317 -0
  31. package/dist-client/components/kpi-single-boxplot-chart.js.map +1 -0
  32. package/dist-client/components/sv-pagenation-control.d.ts +18 -0
  33. package/dist-client/components/sv-pagenation-control.js +142 -0
  34. package/dist-client/components/sv-pagenation-control.js.map +1 -0
  35. package/dist-client/icons/menu-icons.d.ts +6 -0
  36. package/dist-client/icons/menu-icons.js +42 -0
  37. package/dist-client/icons/menu-icons.js.map +1 -1
  38. package/dist-client/menu.d.ts +23 -1
  39. package/dist-client/menu.js +57 -2
  40. package/dist-client/menu.js.map +1 -1
  41. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.d.ts +58 -0
  42. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js +731 -0
  43. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js.map +1 -0
  44. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.d.ts +23 -0
  45. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js +76 -0
  46. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js.map +1 -0
  47. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.d.ts +69 -0
  48. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js +385 -0
  49. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js.map +1 -0
  50. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.d.ts +12 -0
  51. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js +174 -0
  52. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js.map +1 -0
  53. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.d.ts +41 -0
  54. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js +191 -0
  55. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js.map +1 -0
  56. package/dist-client/pages/kpi-value/kpi-value-importer.d.ts +23 -0
  57. package/dist-client/pages/kpi-value/kpi-value-importer.js +93 -0
  58. package/dist-client/pages/kpi-value/kpi-value-importer.js.map +1 -0
  59. package/dist-client/pages/kpi-value/kpi-value-list-page.d.ts +72 -0
  60. package/dist-client/pages/kpi-value/kpi-value-list-page.js +465 -0
  61. package/dist-client/pages/kpi-value/kpi-value-list-page.js.map +1 -0
  62. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.d.ts +13 -0
  63. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js +248 -0
  64. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js.map +1 -0
  65. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.d.ts +12 -0
  66. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js +242 -0
  67. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js.map +1 -0
  68. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.d.ts +15 -0
  69. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.js +222 -0
  70. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.js.map +1 -0
  71. package/dist-client/pages/sv-project-complete.d.ts +20 -0
  72. package/dist-client/pages/sv-project-complete.js +207 -0
  73. package/dist-client/pages/sv-project-complete.js.map +1 -0
  74. package/dist-client/pages/sv-project-completed-list.d.ts +24 -0
  75. package/dist-client/pages/sv-project-completed-list.js +357 -0
  76. package/dist-client/pages/sv-project-completed-list.js.map +1 -0
  77. package/dist-client/pages/sv-project-detail.d.ts +16 -0
  78. package/dist-client/pages/sv-project-detail.js +558 -0
  79. package/dist-client/pages/sv-project-detail.js.map +1 -0
  80. package/dist-client/pages/sv-project-list.d.ts +157 -0
  81. package/dist-client/pages/sv-project-list.js +431 -0
  82. package/dist-client/pages/sv-project-list.js.map +1 -0
  83. package/dist-client/route.d.ts +1 -1
  84. package/dist-client/route.js +22 -1
  85. package/dist-client/route.js.map +1 -1
  86. package/dist-client/shared/complete-api.d.ts +8 -0
  87. package/dist-client/shared/complete-api.js +177 -0
  88. package/dist-client/shared/complete-api.js.map +1 -0
  89. package/dist-client/shared/func.d.ts +2 -0
  90. package/dist-client/shared/func.js +22 -0
  91. package/dist-client/shared/func.js.map +1 -0
  92. package/dist-client/themes/dark.css +24 -24
  93. package/dist-client/themes/light.css +23 -23
  94. package/dist-client/tsconfig.tsbuildinfo +1 -1
  95. package/dist-client/viewparts/menu-tools.d.ts +37 -1
  96. package/dist-client/viewparts/menu-tools.js +348 -15
  97. package/dist-client/viewparts/menu-tools.js.map +1 -1
  98. package/dist-server/index.d.ts +2 -0
  99. package/dist-server/index.js +5 -0
  100. package/dist-server/index.js.map +1 -1
  101. package/dist-server/migrations/index.d.ts +1 -0
  102. package/dist-server/migrations/index.js +12 -0
  103. package/dist-server/migrations/index.js.map +1 -0
  104. package/dist-server/scripts/calculate-kpi-scores.d.ts +10 -0
  105. package/dist-server/scripts/calculate-kpi-scores.js +271 -0
  106. package/dist-server/scripts/calculate-kpi-scores.js.map +1 -0
  107. package/dist-server/scripts/load-grade-data-migration.d.ts +10 -0
  108. package/dist-server/scripts/load-grade-data-migration.js +194 -0
  109. package/dist-server/scripts/load-grade-data-migration.js.map +1 -0
  110. package/dist-server/scripts/propagate-parent-kpi-values.d.ts +10 -0
  111. package/dist-server/scripts/propagate-parent-kpi-values.js +440 -0
  112. package/dist-server/scripts/propagate-parent-kpi-values.js.map +1 -0
  113. package/dist-server/service/index.d.ts +6 -0
  114. package/dist-server/service/index.js +21 -0
  115. package/dist-server/service/index.js.map +1 -0
  116. package/dist-server/service/kpi-metric-value/index.d.ts +4 -0
  117. package/dist-server/service/kpi-metric-value/index.js +8 -0
  118. package/dist-server/service/kpi-metric-value/index.js.map +1 -0
  119. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.d.ts +8 -0
  120. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js +120 -0
  121. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js.map +1 -0
  122. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.d.ts +7 -0
  123. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js +47 -0
  124. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js.map +1 -0
  125. package/dist-server/service/kpi-value/index.d.ts +3 -0
  126. package/dist-server/service/kpi-value/index.js +7 -0
  127. package/dist-server/service/kpi-value/index.js.map +1 -0
  128. package/dist-server/service/kpi-value/kpi-value-query.d.ts +7 -0
  129. package/dist-server/service/kpi-value/kpi-value-query.js +47 -0
  130. package/dist-server/service/kpi-value/kpi-value-query.js.map +1 -0
  131. package/dist-server/tsconfig.tsbuildinfo +1 -1
  132. package/package.json +62 -51
  133. package/schema.graphql +11497 -3215
  134. package/things-factory.config.js +7 -1
@@ -0,0 +1,248 @@
1
+ import { __decorate, __metadata } from "tslib";
2
+ import { css, html, LitElement } from 'lit';
3
+ import { customElement, property } from 'lit/decorators.js';
4
+ import { calcDiff, calcDateDiff } from '../../shared/func';
5
+ import { getKpiMetricValues, getKpiMetrics, updateProjectCompleteStep1 } from '../../shared/complete-api';
6
+ import moment from 'moment-timezone';
7
+ import { notify } from '@operato/layout';
8
+ const KPI_METRIC_KEY_MAPPING = [
9
+ { label: '공사기간', projectKey: 'constructionPeriod' },
10
+ { label: '총 근로자수', projectKey: 'totalWorkerCount' },
11
+ { label: '연간 근로자 수', projectKey: 'annualWorkerCount' },
12
+ { label: '투입인력', projectKey: 'workerCount' },
13
+ { label: '재해 건수', projectKey: 'accidentCount' },
14
+ { label: '일정 이탈 수준', projectKey: 'scheduleDeviationLevel' },
15
+ { label: '총 건설 폐기물 발생량', projectKey: 'totalConstructionWasteAmount' },
16
+ { label: '용적율', projectKey: 'floorAreaRatio' },
17
+ { label: '총 설계 변경 건', projectKey: 'designChangeCount' },
18
+ { label: '공사비', projectKey: 'constructionCost' },
19
+ { label: '연면적', projectKey: 'area' },
20
+ { label: '지상층수', projectKey: 'upperFloorCount' },
21
+ { label: '지하층수', projectKey: 'lowerFloorCount' }
22
+ ];
23
+ let SvProjectCompleteTab1Plan = class SvProjectCompleteTab1Plan extends LitElement {
24
+ constructor() {
25
+ super(...arguments);
26
+ this.kpiMetricValues = [];
27
+ this.kpiMetrics = [];
28
+ this.project = {};
29
+ }
30
+ render() {
31
+ return html `
32
+ <div class="title">
33
+ <div>
34
+ 당초 계획 대비 실제 진행 과정에서 변동된 공사비, 공기(공사기간), 면적, 기타 주요 항목을 현실에 맞게 수정·입력합니다.
35
+ <br />이 정보는 성과 분석, KPI 평가, 통계 산출 등에 기준값으로 사용되므로, 가능한 한 실제 값 기준으로 정확히
36
+ 입력해주시기 바랍니다.
37
+ </div>
38
+ </div>
39
+
40
+ <div class="rows">
41
+ <div class="row header">
42
+ <div class="header-label">기본정보</div>
43
+ <div class="header-label">계획</div>
44
+ <div class="header-label">실제</div>
45
+ <div class="header-label">편차</div>
46
+ </div>
47
+
48
+ ${this.kpiMetrics.map(metric => {
49
+ var _a, _b, _c;
50
+ // 상수로 정의된 매핑 정보에서 metric.name으로 projectKey를 찾음
51
+ const projectKey = ((_a = KPI_METRIC_KEY_MAPPING.find(item => item.label === metric.name)) === null || _a === void 0 ? void 0 : _a.projectKey) || '';
52
+ const kpiMetricValue = this.kpiMetricValues.find((item) => item.metricId === metric.id) || {};
53
+ // planValue는 project에서 찾고 없으면 buildingComplex의 값에서 찾음
54
+ let planValue = this.project[projectKey] || ((_c = (_b = this.project) === null || _b === void 0 ? void 0 : _b.buildingComplex) === null || _c === void 0 ? void 0 : _c[projectKey]) || 0;
55
+ // 공사기간은 기간을 일 단위로 계산
56
+ if (projectKey === 'constructionPeriod') {
57
+ planValue = calcDateDiff(this.project.startDate, this.project.endDate);
58
+ }
59
+ const diffValue = calcDiff(planValue, kpiMetricValue.value);
60
+ const diffClass = diffValue === 0 ? '' : diffValue > 0 ? 'plus' : 'minus';
61
+ const diffSign = diffValue === 0 ? '' : diffValue > 0 ? '+' : '-';
62
+ const unit = kpiMetricValue.unit || metric.unit || '';
63
+ return html `<div class="row">
64
+ <div class="label">• ${metric.name}</div>
65
+ <div class="cell"><input .value=${planValue} disabled /> ${unit}</div>
66
+ <div class="cell">
67
+ <input numeric .value=${kpiMetricValue.value || 0} @input=${(e) => this._onInputChange(e, metric)} />
68
+ ${unit}
69
+ </div>
70
+ <div class="unit ${diffClass}">${diffSign} ${Math.abs(diffValue).toLocaleString()} ${unit}</div>
71
+ </div>`;
72
+ })}
73
+
74
+ <div class="button-line">
75
+ <div class="ghost-btn" @click=${this._reset}>초기화</div>
76
+ <div class="ghost-btn secondary" @click=${() => this._save()}>저장</div>
77
+ </div>
78
+ </div>
79
+ `;
80
+ }
81
+ willUpdate(changedProperties) {
82
+ var _a;
83
+ super.willUpdate(changedProperties);
84
+ // project가 변경되고, project.id가 존재하면 데이터 로드
85
+ if (changedProperties.has('project') && ((_a = this.project) === null || _a === void 0 ? void 0 : _a.id)) {
86
+ this._getInitData();
87
+ }
88
+ }
89
+ async _getInitData() {
90
+ const kpiMetrics = await getKpiMetrics();
91
+ this.kpiMetrics = kpiMetrics.filter(item => !item.name.includes('평가')) || []; // 평가 텍스트가 들어간건 제외
92
+ this.kpiMetricValues = await getKpiMetricValues(this.project.id);
93
+ }
94
+ // Input 요소의 값이 변경될 때 호출되는 콜백 함수
95
+ _onInputChange(event, metric) {
96
+ const target = event.target;
97
+ let inputVal = target.value;
98
+ // 숫자 타입은 다른 문자 입력 제거
99
+ if (target.hasAttribute('numeric')) {
100
+ inputVal = Number(inputVal.replace(/[^\d.]/g, ''));
101
+ }
102
+ // 기존 배열에서 해당 id를 가진 항목을 찾음
103
+ const existingItemIndex = this.kpiMetricValues.findIndex((item) => item.metricId === metric.id);
104
+ if (existingItemIndex !== -1) {
105
+ // 기존 항목이 있으면 업데이트
106
+ this.kpiMetricValues = this.kpiMetricValues.map((item) => item.metricId === metric.id ? Object.assign(Object.assign({}, item), { value: inputVal }) : item);
107
+ }
108
+ else {
109
+ // 기존 항목이 없으면 새로 추가
110
+ this.kpiMetricValues = [
111
+ ...this.kpiMetricValues,
112
+ {
113
+ id: crypto.randomUUID(),
114
+ value: inputVal,
115
+ metricId: metric.id,
116
+ unit: metric.unit || '',
117
+ org: this.project.id, // 프로젝트 ID 추가
118
+ periodType: metric.periodType,
119
+ valueDate: moment().tz('Asia/Seoul').format('YYYY-MM-DD')
120
+ }
121
+ ];
122
+ }
123
+ }
124
+ async _save() {
125
+ const response = await updateProjectCompleteStep1(this.kpiMetricValues);
126
+ if (!response.errors) {
127
+ notify({ message: '저장되었습니다.' });
128
+ }
129
+ }
130
+ _reset() {
131
+ this._getInitData();
132
+ }
133
+ };
134
+ SvProjectCompleteTab1Plan.styles = [
135
+ css `
136
+ :host {
137
+ display: block;
138
+ }
139
+ .title {
140
+ color: #212529;
141
+ font-size: 13px;
142
+ font-weight: 400;
143
+ line-height: 24px;
144
+ text-align: center;
145
+ }
146
+
147
+ .rows {
148
+ display: flex;
149
+ flex-direction: column;
150
+ padding: 8px 6px;
151
+ }
152
+ .row.header {
153
+ min-height: 35px;
154
+ background: #f3f3fa;
155
+ border-top: 2px #0c4da2 solid;
156
+ grid-template-columns: 220px 1fr 1fr 200px;
157
+ padding: 0px 25px;
158
+
159
+ .header-label {
160
+ color: #212529;
161
+ text-align: center;
162
+ }
163
+ }
164
+ .row {
165
+ display: grid;
166
+ grid-template-columns: 220px 1fr 1fr 200px;
167
+ gap: 6px 10px;
168
+ align-items: center;
169
+ padding: 8px 25px;
170
+ border-bottom: 1px rgba(0, 0, 0, 0.1) solid;
171
+
172
+ .cell {
173
+ display: flex;
174
+ align-items: center;
175
+ justify-content: center;
176
+ }
177
+ }
178
+ .label {
179
+ color: #35618e;
180
+ font-size: 16px;
181
+ letter-spacing: -0.05em;
182
+ white-space: nowrap;
183
+ display: flex;
184
+ justify-content: center;
185
+ }
186
+ input {
187
+ padding: 6px 8px;
188
+ border: 1px solid rgba(0, 0, 0, 0.1);
189
+ border-radius: 5px;
190
+ background: #ffffff;
191
+ color: #212529;
192
+ font-size: 16px;
193
+ margin-right: 5px;
194
+
195
+ &:disabled {
196
+ background: #f6f6f6;
197
+ }
198
+ }
199
+ .unit {
200
+ text-align: center;
201
+ color: #212529;
202
+ }
203
+ .plus {
204
+ color: #e13232;
205
+ font-weight: 700;
206
+ }
207
+ .minus {
208
+ color: #1e88e5;
209
+ font-weight: 700;
210
+ }
211
+ .button-line {
212
+ display: flex;
213
+ justify-content: center;
214
+ gap: 10px;
215
+ margin-top: 16px;
216
+ }
217
+ .ghost-btn {
218
+ display: inline-flex;
219
+ align-items: center;
220
+ gap: 6px;
221
+ padding: 6px 10px;
222
+ background: #35618e;
223
+ color: #ffffff;
224
+ border-radius: 5px;
225
+ cursor: pointer;
226
+ }
227
+ .ghost-btn.secondary {
228
+ background: #24be7b;
229
+ }
230
+ `
231
+ ];
232
+ __decorate([
233
+ property({ type: Array }),
234
+ __metadata("design:type", Object)
235
+ ], SvProjectCompleteTab1Plan.prototype, "kpiMetricValues", void 0);
236
+ __decorate([
237
+ property({ type: Array }),
238
+ __metadata("design:type", Object)
239
+ ], SvProjectCompleteTab1Plan.prototype, "kpiMetrics", void 0);
240
+ __decorate([
241
+ property({ type: Object }),
242
+ __metadata("design:type", Object)
243
+ ], SvProjectCompleteTab1Plan.prototype, "project", void 0);
244
+ SvProjectCompleteTab1Plan = __decorate([
245
+ customElement('sv-pc-tab1-plan')
246
+ ], SvProjectCompleteTab1Plan);
247
+ export { SvProjectCompleteTab1Plan };
248
+ //# sourceMappingURL=pc-tab1-plan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pc-tab1-plan.js","sourceRoot":"","sources":["../../../client/pages/project-complete-tabs/pc-tab1-plan.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, LitElement } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { calcDiff, calcDateDiff } from '../../shared/func'\nimport { getKpiMetricValues, getKpiMetrics, updateProjectCompleteStep1 } from '../../shared/complete-api'\nimport moment from 'moment-timezone'\nimport { notify } from '@operato/layout'\n\nconst KPI_METRIC_KEY_MAPPING = [\n { label: '공사기간', projectKey: 'constructionPeriod' },\n { label: '총 근로자수', projectKey: 'totalWorkerCount' },\n { label: '연간 근로자 수', projectKey: 'annualWorkerCount' },\n { label: '투입인력', projectKey: 'workerCount' },\n { label: '재해 건수', projectKey: 'accidentCount' },\n { label: '일정 이탈 수준', projectKey: 'scheduleDeviationLevel' },\n { label: '총 건설 폐기물 발생량', projectKey: 'totalConstructionWasteAmount' },\n { label: '용적율', projectKey: 'floorAreaRatio' },\n { label: '총 설계 변경 건', projectKey: 'designChangeCount' },\n { label: '공사비', projectKey: 'constructionCost' },\n { label: '연면적', projectKey: 'area' },\n { label: '지상층수', projectKey: 'upperFloorCount' },\n { label: '지하층수', projectKey: 'lowerFloorCount' }\n]\n\n@customElement('sv-pc-tab1-plan')\nexport class SvProjectCompleteTab1Plan extends LitElement {\n static styles = [\n css`\n :host {\n display: block;\n }\n .title {\n color: #212529;\n font-size: 13px;\n font-weight: 400;\n line-height: 24px;\n text-align: center;\n }\n\n .rows {\n display: flex;\n flex-direction: column;\n padding: 8px 6px;\n }\n .row.header {\n min-height: 35px;\n background: #f3f3fa;\n border-top: 2px #0c4da2 solid;\n grid-template-columns: 220px 1fr 1fr 200px;\n padding: 0px 25px;\n\n .header-label {\n color: #212529;\n text-align: center;\n }\n }\n .row {\n display: grid;\n grid-template-columns: 220px 1fr 1fr 200px;\n gap: 6px 10px;\n align-items: center;\n padding: 8px 25px;\n border-bottom: 1px rgba(0, 0, 0, 0.1) solid;\n\n .cell {\n display: flex;\n align-items: center;\n justify-content: center;\n }\n }\n .label {\n color: #35618e;\n font-size: 16px;\n letter-spacing: -0.05em;\n white-space: nowrap;\n display: flex;\n justify-content: center;\n }\n input {\n padding: 6px 8px;\n border: 1px solid rgba(0, 0, 0, 0.1);\n border-radius: 5px;\n background: #ffffff;\n color: #212529;\n font-size: 16px;\n margin-right: 5px;\n\n &:disabled {\n background: #f6f6f6;\n }\n }\n .unit {\n text-align: center;\n color: #212529;\n }\n .plus {\n color: #e13232;\n font-weight: 700;\n }\n .minus {\n color: #1e88e5;\n font-weight: 700;\n }\n .button-line {\n display: flex;\n justify-content: center;\n gap: 10px;\n margin-top: 16px;\n }\n .ghost-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 10px;\n background: #35618e;\n color: #ffffff;\n border-radius: 5px;\n cursor: pointer;\n }\n .ghost-btn.secondary {\n background: #24be7b;\n }\n `\n ]\n\n @property({ type: Array }) kpiMetricValues: any = []\n @property({ type: Array }) kpiMetrics: any = []\n @property({ type: Object }) project: any = {}\n\n render() {\n return html`\n <div class=\"title\">\n <div>\n 당초 계획 대비 실제 진행 과정에서 변동된 공사비, 공기(공사기간), 면적, 기타 주요 항목을 현실에 맞게 수정·입력합니다.\n <br />이 정보는 성과 분석, KPI 평가, 통계 산출 등에 기준값으로 사용되므로, 가능한 한 실제 값 기준으로 정확히\n 입력해주시기 바랍니다.\n </div>\n </div>\n\n <div class=\"rows\">\n <div class=\"row header\">\n <div class=\"header-label\">기본정보</div>\n <div class=\"header-label\">계획</div>\n <div class=\"header-label\">실제</div>\n <div class=\"header-label\">편차</div>\n </div>\n\n ${this.kpiMetrics.map(metric => {\n // 상수로 정의된 매핑 정보에서 metric.name으로 projectKey를 찾음\n const projectKey = KPI_METRIC_KEY_MAPPING.find(item => item.label === metric.name)?.projectKey || ''\n const kpiMetricValue = this.kpiMetricValues.find((item: any) => item.metricId === metric.id) || {}\n\n // planValue는 project에서 찾고 없으면 buildingComplex의 값에서 찾음\n let planValue = this.project[projectKey] || this.project?.buildingComplex?.[projectKey] || 0\n\n // 공사기간은 기간을 일 단위로 계산\n if (projectKey === 'constructionPeriod') {\n planValue = calcDateDiff(this.project.startDate, this.project.endDate)\n }\n\n const diffValue = calcDiff(planValue, kpiMetricValue.value)\n const diffClass = diffValue === 0 ? '' : diffValue > 0 ? 'plus' : 'minus'\n const diffSign = diffValue === 0 ? '' : diffValue > 0 ? '+' : '-'\n\n const unit = kpiMetricValue.unit || metric.unit || ''\n\n return html`<div class=\"row\">\n <div class=\"label\">• ${metric.name}</div>\n <div class=\"cell\"><input .value=${planValue} disabled /> ${unit}</div>\n <div class=\"cell\">\n <input numeric .value=${kpiMetricValue.value || 0} @input=${(e: InputEvent) => this._onInputChange(e, metric)} />\n ${unit}\n </div>\n <div class=\"unit ${diffClass}\">${diffSign} ${Math.abs(diffValue).toLocaleString()} ${unit}</div>\n </div>`\n })}\n\n <div class=\"button-line\">\n <div class=\"ghost-btn\" @click=${this._reset}>초기화</div>\n <div class=\"ghost-btn secondary\" @click=${() => this._save()}>저장</div>\n </div>\n </div>\n `\n }\n\n willUpdate(changedProperties: Map<string, any>) {\n super.willUpdate(changedProperties)\n\n // project가 변경되고, project.id가 존재하면 데이터 로드\n if (changedProperties.has('project') && this.project?.id) {\n this._getInitData()\n }\n }\n\n private async _getInitData() {\n const kpiMetrics = await getKpiMetrics()\n this.kpiMetrics = kpiMetrics.filter(item => !item.name.includes('평가')) || [] // 평가 텍스트가 들어간건 제외\n this.kpiMetricValues = await getKpiMetricValues(this.project.id)\n }\n\n // Input 요소의 값이 변경될 때 호출되는 콜백 함수\n private _onInputChange(event: InputEvent, metric: any) {\n const target = event.target as HTMLInputElement\n let inputVal: any = target.value\n\n // 숫자 타입은 다른 문자 입력 제거\n if (target.hasAttribute('numeric')) {\n inputVal = Number(inputVal.replace(/[^\\d.]/g, ''))\n }\n\n // 기존 배열에서 해당 id를 가진 항목을 찾음\n const existingItemIndex = this.kpiMetricValues.findIndex((item: any) => item.metricId === metric.id)\n\n if (existingItemIndex !== -1) {\n // 기존 항목이 있으면 업데이트\n this.kpiMetricValues = this.kpiMetricValues.map((item: any) =>\n item.metricId === metric.id ? { ...item, value: inputVal } : item\n )\n } else {\n // 기존 항목이 없으면 새로 추가\n this.kpiMetricValues = [\n ...this.kpiMetricValues,\n {\n id: crypto.randomUUID(),\n value: inputVal,\n metricId: metric.id,\n unit: metric.unit || '',\n org: this.project.id, // 프로젝트 ID 추가\n periodType: metric.periodType,\n valueDate: moment().tz('Asia/Seoul').format('YYYY-MM-DD')\n }\n ]\n }\n }\n\n private async _save() {\n const response = await updateProjectCompleteStep1(this.kpiMetricValues)\n if (!response.errors) {\n notify({ message: '저장되었습니다.' })\n }\n }\n\n private _reset() {\n this._getInitData()\n }\n}\n"]}
@@ -0,0 +1,12 @@
1
+ import { LitElement } from 'lit';
2
+ export declare class SvProjectCompleteTab2Rating extends LitElement {
3
+ static styles: import("lit").CSSResult[];
4
+ kpiCategories: any;
5
+ kpiMetrics: any;
6
+ render(): import("lit-html").TemplateResult<1>;
7
+ connectedCallback(): void;
8
+ private _getInitData;
9
+ private _setRatingHalf;
10
+ private _save;
11
+ private _reset;
12
+ }
@@ -0,0 +1,242 @@
1
+ import { __decorate, __metadata } from "tslib";
2
+ import { css, html, LitElement } from 'lit';
3
+ import { customElement, property } from 'lit/decorators.js';
4
+ import { calcDiff } from '../../shared/func';
5
+ import { getKpiCategories, getKpiMetrics, updateProjectCompleteStep2 } from '../../shared/complete-api';
6
+ import { notify } from '@operato/layout';
7
+ let SvProjectCompleteTab2Rating = class SvProjectCompleteTab2Rating extends LitElement {
8
+ constructor() {
9
+ super(...arguments);
10
+ this.kpiCategories = [];
11
+ this.kpiMetrics = [];
12
+ }
13
+ render() {
14
+ return html `
15
+ <div class="title">
16
+ <div>
17
+ 당초 계획 대비 실제 진행 과정에서 변동된 공사비, 공기(공사기간), 면적, 기타 주요 항목을 현실에 맞게 수정·입력합니다.
18
+ <br />이 정보는 성과 분석, KPI 평가, 통계 산출 등에 기준값으로 사용되므로, 가능한 한 실제 값 기준으로 정확히
19
+ 입력해주시기 바랍니다.
20
+ </div>
21
+ </div>
22
+
23
+ <div class="rows">
24
+ <div class="row header">
25
+ <div class="header-label">성과영역</div>
26
+ <div class="header-label">기존 월간 수준 평가 평균</div>
27
+ <div class="header-label">완료 평가</div>
28
+ <div class="header-label">편차</div>
29
+ </div>
30
+
31
+ ${this.kpiCategories.map((item, idx) => {
32
+ const completeScore = this.kpiMetrics.find(v => v.id === item.id);
33
+ const diff = calcDiff(item.value, completeScore === null || completeScore === void 0 ? void 0 : completeScore.value);
34
+ const diffClass = diff === 0 ? '' : diff > 0 ? 'plus' : 'minus';
35
+ const diffSign = diff === 0 ? '' : diff > 0 ? '+' : '-';
36
+ return html `
37
+ <div class="row">
38
+ <div class="label">• ${item.name}</div>
39
+ <div class="cell">${(item === null || item === void 0 ? void 0 : item.value) || 0}</div>
40
+ <div class="stars" @mouseleave=${() => { }}>
41
+ ${[1, 2, 3, 4, 5].map(starIndex => {
42
+ var _a;
43
+ const score10 = Number((_a = completeScore === null || completeScore === void 0 ? void 0 : completeScore.value) !== null && _a !== void 0 ? _a : 0); // 0~10
44
+ const fullUntil = Math.floor(score10 / 2); // 정수 별 개수
45
+ const hasHalf = score10 % 2 === 1;
46
+ const fillForThis = starIndex <= fullUntil ? 100 : starIndex === fullUntil + 1 && hasHalf ? 44 : 0;
47
+ return html `
48
+ <span class="star-wrap">
49
+ <span class="star-base">☆</span>
50
+ <span class="star-fill" style="width: ${fillForThis}%;">★</span>
51
+ <span class="click-half left" @click=${() => this._setRatingHalf(item.id, (starIndex - 1) * 2 + 1)}></span>
52
+ <span class="click-half right" @click=${() => this._setRatingHalf(item.id, starIndex * 2)}></span>
53
+ </span>
54
+ `;
55
+ })}
56
+ </div>
57
+ <div class="unit ${diffClass}">${diffSign} ${Math.abs(diff).toLocaleString()}</div>
58
+ </div>
59
+ `;
60
+ })}
61
+
62
+ <div class="button-line">
63
+ <div class="ghost-btn" @click=${this._reset}>초기화</div>
64
+ <div class="ghost-btn secondary" @click=${() => this._save()}>저장</div>
65
+ </div>
66
+ </div>
67
+ `;
68
+ }
69
+ connectedCallback() {
70
+ super.connectedCallback();
71
+ this._getInitData();
72
+ }
73
+ async _getInitData() {
74
+ const kpiMetrics = await getKpiMetrics();
75
+ this.kpiMetrics = kpiMetrics.filter(item => !item.name.includes('평가')) || []; // 평가 텍스트가 들어간건 제외
76
+ this.kpiCategories = await getKpiCategories();
77
+ }
78
+ _setRatingHalf(itemId, score10) {
79
+ // score10: 0~10, 0.5 단위
80
+ const next = Array.isArray(this.kpiMetrics) ? [...this.kpiMetrics] : [];
81
+ const idx = next.findIndex((v) => v.id === itemId);
82
+ if (idx >= 0)
83
+ next[idx] = Object.assign(Object.assign({}, next[idx]), { value: score10 });
84
+ else
85
+ next.push({ id: itemId, value: score10 });
86
+ this.kpiMetrics = next;
87
+ this.dispatchEvent(new CustomEvent('complete-data-change', { detail: { tab: 2, data: this.kpiMetrics } }));
88
+ }
89
+ async _save() {
90
+ const response = await updateProjectCompleteStep2(this.kpiCategories);
91
+ if (!response.errors) {
92
+ notify({ message: '저장되었습니다.' });
93
+ }
94
+ }
95
+ _reset() {
96
+ this._getInitData();
97
+ }
98
+ };
99
+ SvProjectCompleteTab2Rating.styles = [
100
+ css `
101
+ :host {
102
+ display: block;
103
+ }
104
+ .title {
105
+ color: #212529;
106
+ font-size: 13px;
107
+ font-weight: 400;
108
+ line-height: 24px;
109
+ text-align: center;
110
+ }
111
+
112
+ .rows {
113
+ display: flex;
114
+ flex-direction: column;
115
+ padding: 8px 6px;
116
+ }
117
+ .row.header {
118
+ min-height: 35px;
119
+ background: #f3f3fa;
120
+ border-top: 2px #0c4da2 solid;
121
+ grid-template-columns: 300px 1fr 1fr 200px;
122
+ padding: 0px 25px;
123
+
124
+ .header-label {
125
+ color: #212529;
126
+ text-align: center;
127
+ }
128
+ }
129
+ .row {
130
+ display: grid;
131
+ grid-template-columns: 300px 1fr 1fr 200px;
132
+ gap: 6px 10px;
133
+ padding: 8px 25px;
134
+ align-items: center;
135
+ border-bottom: 1px rgba(0, 0, 0, 0.1) solid;
136
+
137
+ .cell {
138
+ display: flex;
139
+ align-items: center;
140
+ justify-content: center;
141
+ }
142
+ }
143
+ .label {
144
+ color: #35618e;
145
+ font-size: 16px;
146
+ letter-spacing: -0.05em;
147
+ white-space: nowrap;
148
+ display: flex;
149
+ justify-content: center;
150
+ }
151
+ .stars {
152
+ display: inline-flex;
153
+ gap: 6px;
154
+ align-items: center;
155
+ justify-content: center;
156
+ cursor: pointer;
157
+ }
158
+ .star-wrap {
159
+ position: relative;
160
+ width: 28px;
161
+ height: 28px;
162
+ display: inline-block;
163
+ }
164
+ .star-base,
165
+ .star-fill {
166
+ position: absolute;
167
+ top: 0;
168
+ left: 0;
169
+ font-size: 28px;
170
+ line-height: 28px;
171
+ user-select: none;
172
+ }
173
+ .star-base {
174
+ color: #d0d7e2;
175
+ }
176
+ .star-fill {
177
+ color: #ffb400;
178
+ overflow: hidden;
179
+ width: 0%;
180
+ }
181
+ .click-half {
182
+ position: absolute;
183
+ top: 0;
184
+ width: 50%;
185
+ height: 100%;
186
+ }
187
+ .click-half.left {
188
+ left: 0;
189
+ }
190
+ .click-half.right {
191
+ right: 0;
192
+ }
193
+ .score {
194
+ color: #212529;
195
+ text-align: right;
196
+ }
197
+ .plus {
198
+ color: #e13232;
199
+ font-weight: 700;
200
+ }
201
+ .minus {
202
+ color: #1e88e5;
203
+ font-weight: 700;
204
+ }
205
+ .unit {
206
+ text-align: center;
207
+ color: #212529;
208
+ }
209
+ .button-line {
210
+ display: flex;
211
+ justify-content: center;
212
+ gap: 10px;
213
+ margin-top: 16px;
214
+ }
215
+ .ghost-btn {
216
+ display: inline-flex;
217
+ align-items: center;
218
+ gap: 6px;
219
+ padding: 6px 10px;
220
+ background: #35618e;
221
+ color: #ffffff;
222
+ border-radius: 5px;
223
+ cursor: pointer;
224
+ }
225
+ .ghost-btn.secondary {
226
+ background: #24be7b;
227
+ }
228
+ `
229
+ ];
230
+ __decorate([
231
+ property({ type: Array }),
232
+ __metadata("design:type", Object)
233
+ ], SvProjectCompleteTab2Rating.prototype, "kpiCategories", void 0);
234
+ __decorate([
235
+ property({ type: Array }),
236
+ __metadata("design:type", Object)
237
+ ], SvProjectCompleteTab2Rating.prototype, "kpiMetrics", void 0);
238
+ SvProjectCompleteTab2Rating = __decorate([
239
+ customElement('sv-pc-tab2-rating')
240
+ ], SvProjectCompleteTab2Rating);
241
+ export { SvProjectCompleteTab2Rating };
242
+ //# sourceMappingURL=pc-tab2-rating.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pc-tab2-rating.js","sourceRoot":"","sources":["../../../client/pages/project-complete-tabs/pc-tab2-rating.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, LitElement } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { calcDiff } from '../../shared/func'\nimport { getKpiCategories, getKpiMetrics, updateProjectCompleteStep2 } from '../../shared/complete-api'\nimport { notify } from '@operato/layout'\n\n@customElement('sv-pc-tab2-rating')\nexport class SvProjectCompleteTab2Rating extends LitElement {\n static styles = [\n css`\n :host {\n display: block;\n }\n .title {\n color: #212529;\n font-size: 13px;\n font-weight: 400;\n line-height: 24px;\n text-align: center;\n }\n\n .rows {\n display: flex;\n flex-direction: column;\n padding: 8px 6px;\n }\n .row.header {\n min-height: 35px;\n background: #f3f3fa;\n border-top: 2px #0c4da2 solid;\n grid-template-columns: 300px 1fr 1fr 200px;\n padding: 0px 25px;\n\n .header-label {\n color: #212529;\n text-align: center;\n }\n }\n .row {\n display: grid;\n grid-template-columns: 300px 1fr 1fr 200px;\n gap: 6px 10px;\n padding: 8px 25px;\n align-items: center;\n border-bottom: 1px rgba(0, 0, 0, 0.1) solid;\n\n .cell {\n display: flex;\n align-items: center;\n justify-content: center;\n }\n }\n .label {\n color: #35618e;\n font-size: 16px;\n letter-spacing: -0.05em;\n white-space: nowrap;\n display: flex;\n justify-content: center;\n }\n .stars {\n display: inline-flex;\n gap: 6px;\n align-items: center;\n justify-content: center;\n cursor: pointer;\n }\n .star-wrap {\n position: relative;\n width: 28px;\n height: 28px;\n display: inline-block;\n }\n .star-base,\n .star-fill {\n position: absolute;\n top: 0;\n left: 0;\n font-size: 28px;\n line-height: 28px;\n user-select: none;\n }\n .star-base {\n color: #d0d7e2;\n }\n .star-fill {\n color: #ffb400;\n overflow: hidden;\n width: 0%;\n }\n .click-half {\n position: absolute;\n top: 0;\n width: 50%;\n height: 100%;\n }\n .click-half.left {\n left: 0;\n }\n .click-half.right {\n right: 0;\n }\n .score {\n color: #212529;\n text-align: right;\n }\n .plus {\n color: #e13232;\n font-weight: 700;\n }\n .minus {\n color: #1e88e5;\n font-weight: 700;\n }\n .unit {\n text-align: center;\n color: #212529;\n }\n .button-line {\n display: flex;\n justify-content: center;\n gap: 10px;\n margin-top: 16px;\n }\n .ghost-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 10px;\n background: #35618e;\n color: #ffffff;\n border-radius: 5px;\n cursor: pointer;\n }\n .ghost-btn.secondary {\n background: #24be7b;\n }\n `\n ]\n\n @property({ type: Array }) kpiCategories: any = []\n @property({ type: Array }) kpiMetrics: any = []\n\n render() {\n return html`\n <div class=\"title\">\n <div>\n 당초 계획 대비 실제 진행 과정에서 변동된 공사비, 공기(공사기간), 면적, 기타 주요 항목을 현실에 맞게 수정·입력합니다.\n <br />이 정보는 성과 분석, KPI 평가, 통계 산출 등에 기준값으로 사용되므로, 가능한 한 실제 값 기준으로 정확히\n 입력해주시기 바랍니다.\n </div>\n </div>\n\n <div class=\"rows\">\n <div class=\"row header\">\n <div class=\"header-label\">성과영역</div>\n <div class=\"header-label\">기존 월간 수준 평가 평균</div>\n <div class=\"header-label\">완료 평가</div>\n <div class=\"header-label\">편차</div>\n </div>\n\n ${this.kpiCategories.map((item, idx) => {\n const completeScore = this.kpiMetrics.find(v => v.id === item.id)\n const diff = calcDiff(item.value, completeScore?.value)\n const diffClass = diff === 0 ? '' : diff > 0 ? 'plus' : 'minus'\n const diffSign = diff === 0 ? '' : diff > 0 ? '+' : '-'\n\n return html`\n <div class=\"row\">\n <div class=\"label\">• ${item.name}</div>\n <div class=\"cell\">${item?.value || 0}</div>\n <div class=\"stars\" @mouseleave=${() => {}}>\n ${[1, 2, 3, 4, 5].map(starIndex => {\n const score10 = Number(completeScore?.value ?? 0) // 0~10\n const fullUntil = Math.floor(score10 / 2) // 정수 별 개수\n const hasHalf = score10 % 2 === 1\n const fillForThis = starIndex <= fullUntil ? 100 : starIndex === fullUntil + 1 && hasHalf ? 44 : 0\n\n return html`\n <span class=\"star-wrap\">\n <span class=\"star-base\">☆</span>\n <span class=\"star-fill\" style=\"width: ${fillForThis}%;\">★</span>\n <span class=\"click-half left\" @click=${() => this._setRatingHalf(item.id, (starIndex - 1) * 2 + 1)}></span>\n <span class=\"click-half right\" @click=${() => this._setRatingHalf(item.id, starIndex * 2)}></span>\n </span>\n `\n })}\n </div>\n <div class=\"unit ${diffClass}\">${diffSign} ${Math.abs(diff).toLocaleString()}</div>\n </div>\n `\n })}\n\n <div class=\"button-line\">\n <div class=\"ghost-btn\" @click=${this._reset}>초기화</div>\n <div class=\"ghost-btn secondary\" @click=${() => this._save()}>저장</div>\n </div>\n </div>\n `\n }\n\n connectedCallback() {\n super.connectedCallback()\n this._getInitData()\n }\n\n private async _getInitData() {\n const kpiMetrics = await getKpiMetrics()\n this.kpiMetrics = kpiMetrics.filter(item => !item.name.includes('평가')) || [] // 평가 텍스트가 들어간건 제외\n this.kpiCategories = await getKpiCategories()\n }\n\n private _setRatingHalf(itemId: string, score10: number) {\n // score10: 0~10, 0.5 단위\n const next = Array.isArray(this.kpiMetrics) ? [...this.kpiMetrics] : []\n const idx = next.findIndex((v: any) => v.id === itemId)\n if (idx >= 0) next[idx] = { ...next[idx], value: score10 }\n else next.push({ id: itemId, value: score10 })\n\n this.kpiMetrics = next\n this.dispatchEvent(new CustomEvent('complete-data-change', { detail: { tab: 2, data: this.kpiMetrics } }))\n }\n\n private async _save() {\n const response = await updateProjectCompleteStep2(this.kpiCategories)\n if (!response.errors) {\n notify({ message: '저장되었습니다.' })\n }\n }\n\n private _reset() {\n this._getInitData()\n }\n}\n"]}
@@ -0,0 +1,15 @@
1
+ import { LitElement } from 'lit';
2
+ export declare class SvProjectCompleteTab3Upload extends LitElement {
3
+ static styles: import("lit").CSSResult[];
4
+ project: any;
5
+ attachment: any;
6
+ private pendingFile;
7
+ render(): import("lit-html").TemplateResult<1>;
8
+ willUpdate(changedProperties: Map<string, any>): void;
9
+ private _getCurrentFile;
10
+ private _save;
11
+ private _removeFile;
12
+ private _reset;
13
+ private _onFileSelect;
14
+ private _uploadFile;
15
+ }