@dssp/dkpi 1.0.0-alpha.59 → 1.0.0-alpha.60
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.
- package/dist-client/pages/project-complete-tabs/pc-tab1-plan.d.ts +1 -0
- package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js +65 -3
- package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js.map +1 -1
- package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js +10 -13
- package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js +2 -0
- package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/schema.graphql +18 -5
|
@@ -20,7 +20,12 @@ const KPI_METRIC_KEY_MAPPING = [
|
|
|
20
20
|
{ label: '지상층수', projectKey: 'upperFloorCount' },
|
|
21
21
|
{ label: '지하층수', projectKey: 'lowerFloorCount' }
|
|
22
22
|
];
|
|
23
|
-
const
|
|
23
|
+
const EXCLUDE_FIELDS = [
|
|
24
|
+
{ id: 'b7a583c0-9de9-4c12-af49-dde65198dfce', name: '계획공사비' },
|
|
25
|
+
{ id: '9767747a-d2ec-4e36-96c4-4a78bba35b98', name: '실제공사비' },
|
|
26
|
+
{ id: '036d6e1c-193e-46bd-8d6e-93f248af8124', name: '계획공사기간' },
|
|
27
|
+
{ id: '5df77618-db44-4da3-a22d-43c067cb5e86', name: '실제공사기간' }
|
|
28
|
+
];
|
|
24
29
|
let SvProjectCompleteTab1Plan = class SvProjectCompleteTab1Plan extends LitElement {
|
|
25
30
|
constructor() {
|
|
26
31
|
super(...arguments);
|
|
@@ -48,6 +53,9 @@ let SvProjectCompleteTab1Plan = class SvProjectCompleteTab1Plan extends LitEleme
|
|
|
48
53
|
|
|
49
54
|
${this.kpiMetrics.map(metric => {
|
|
50
55
|
var _a, _b, _c;
|
|
56
|
+
// 제외 필드는 표시하지 않음
|
|
57
|
+
if (EXCLUDE_FIELDS.some(item => item.id === metric.id))
|
|
58
|
+
return null;
|
|
51
59
|
// 상수로 정의된 매핑 정보에서 metric.name으로 projectKey를 찾음
|
|
52
60
|
const projectKey = ((_a = KPI_METRIC_KEY_MAPPING.find(item => item.label === metric.name)) === null || _a === void 0 ? void 0 : _a.projectKey) || '';
|
|
53
61
|
const kpiMetricValue = this.kpiMetricValues.find((item) => item.metricId === metric.id) || {};
|
|
@@ -57,7 +65,7 @@ let SvProjectCompleteTab1Plan = class SvProjectCompleteTab1Plan extends LitEleme
|
|
|
57
65
|
const unit = kpiMetricValue.unit || metric.unit || '';
|
|
58
66
|
// 공사기간은 기간을 일 단위로 계산
|
|
59
67
|
if (projectKey === 'constructionPeriod') {
|
|
60
|
-
planValue = calcDateDiff(this.project.startDate, this.project.endDate);
|
|
68
|
+
planValue = Math.ceil(calcDateDiff(this.project.startDate, this.project.endDate) / 30);
|
|
61
69
|
}
|
|
62
70
|
else if (!isDisplayInput) {
|
|
63
71
|
planValue = 0;
|
|
@@ -126,8 +134,62 @@ let SvProjectCompleteTab1Plan = class SvProjectCompleteTab1Plan extends LitEleme
|
|
|
126
134
|
];
|
|
127
135
|
}
|
|
128
136
|
}
|
|
137
|
+
_generateExcludeFieldsData() {
|
|
138
|
+
var _a, _b;
|
|
139
|
+
const excludeData = [];
|
|
140
|
+
const constructionCostMetric = this.kpiMetrics.find(metric => { var _a; return ((_a = KPI_METRIC_KEY_MAPPING.find(item => item.label === metric.name)) === null || _a === void 0 ? void 0 : _a.projectKey) === 'constructionCost'; });
|
|
141
|
+
// 공사기간 관련 메트릭을 찾음
|
|
142
|
+
const constructionPeriodMetric = this.kpiMetrics.find(metric => { var _a; return ((_a = KPI_METRIC_KEY_MAPPING.find(item => item.label === metric.name)) === null || _a === void 0 ? void 0 : _a.projectKey) === 'constructionPeriod'; });
|
|
143
|
+
// 실적공사비 값 (사용자가 입력한 constructionCost 값)
|
|
144
|
+
const actualConstructionCostValue = constructionCostMetric
|
|
145
|
+
? ((_a = this.kpiMetricValues.find((item) => item.metricId === constructionCostMetric.id)) === null || _a === void 0 ? void 0 : _a.value) || 0
|
|
146
|
+
: 0;
|
|
147
|
+
// 실제공사기간 값 (사용자가 입력한 constructionPeriod 값)
|
|
148
|
+
const actualConstructionPeriodValue = constructionPeriodMetric
|
|
149
|
+
? ((_b = this.kpiMetricValues.find((item) => item.metricId === constructionPeriodMetric.id)) === null || _b === void 0 ? void 0 : _b.value) || 0
|
|
150
|
+
: 0;
|
|
151
|
+
EXCLUDE_FIELDS.forEach(excludeField => {
|
|
152
|
+
var _a, _b;
|
|
153
|
+
// 해당 excludeField.id에 매칭되는 메트릭 찾기
|
|
154
|
+
const metric = this.kpiMetrics.find(m => m.id === excludeField.id);
|
|
155
|
+
// 기존에 저장된 데이터가 있는지 확인
|
|
156
|
+
const existingValue = this.kpiMetricValues.find((item) => item.metricId === excludeField.id);
|
|
157
|
+
let value = 0;
|
|
158
|
+
if (excludeField.name == '계획공사비') {
|
|
159
|
+
value = ((_b = (_a = this.project) === null || _a === void 0 ? void 0 : _a.buildingComplex) === null || _b === void 0 ? void 0 : _b.constructionCost) || 0;
|
|
160
|
+
}
|
|
161
|
+
else if (excludeField.name == '실제공사비') {
|
|
162
|
+
value = actualConstructionCostValue;
|
|
163
|
+
}
|
|
164
|
+
else if (excludeField.name == '계획공사기간') {
|
|
165
|
+
value = Math.ceil(calcDateDiff(this.project.startDate, this.project.endDate) / 30);
|
|
166
|
+
}
|
|
167
|
+
else if (excludeField.name == '실제공사기간') {
|
|
168
|
+
value = actualConstructionPeriodValue;
|
|
169
|
+
}
|
|
170
|
+
excludeData.push({
|
|
171
|
+
id: (existingValue === null || existingValue === void 0 ? void 0 : existingValue.id) || crypto.randomUUID(), // 기존 데이터가 있으면 해당 id 사용
|
|
172
|
+
value,
|
|
173
|
+
metricId: excludeField.id,
|
|
174
|
+
unit: (metric === null || metric === void 0 ? void 0 : metric.unit) || '',
|
|
175
|
+
org: this.project.id,
|
|
176
|
+
periodType: metric === null || metric === void 0 ? void 0 : metric.periodType,
|
|
177
|
+
valueDate: moment().tz('Asia/Seoul').format('YYYY-MM-DD')
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
return excludeData;
|
|
181
|
+
}
|
|
129
182
|
async _save() {
|
|
130
|
-
|
|
183
|
+
// EXCLUDE_FIELDS에 대한 값들을 자동으로 생성
|
|
184
|
+
const excludeData = this._generateExcludeFieldsData();
|
|
185
|
+
// EXCLUDE_FIELDS에 해당하는 metricId를 추출
|
|
186
|
+
const excludeMetricIds = EXCLUDE_FIELDS.map(field => field.id);
|
|
187
|
+
// kpiMetricValues에서 EXCLUDE_FIELDS에 해당하는 중복 항목들을 제외
|
|
188
|
+
const filteredKpiMetricValues = this.kpiMetricValues.filter((item) => !excludeMetricIds.includes(item.metricId));
|
|
189
|
+
// 필터링된 데이터와 exclude 데이터를 합침
|
|
190
|
+
const allKpiMetricValues = [...excludeData, ...filteredKpiMetricValues];
|
|
191
|
+
console.log('저장할 데이터:', allKpiMetricValues);
|
|
192
|
+
const response = await updateProjectCompleteStep1(allKpiMetricValues);
|
|
131
193
|
if (!response.errors) {
|
|
132
194
|
notify({ message: '저장되었습니다.' });
|
|
133
195
|
}
|
|
@@ -1 +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]\nconst VALID_FIELDS = ['constructionPeriod', 'constructionCost']\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 @state() kpiMetricValues: any = []\n @state() 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 const isDisplayInput = projectKey === 'constructionPeriod' || projectKey === 'constructionCost' // 유효한 계획 필드\n\n // planValue는 project에서 찾고 없으면 buildingComplex의 값에서 찾음\n let planValue = this.project[projectKey] || this.project?.buildingComplex?.[projectKey] || 0\n const unit = kpiMetricValue.unit || metric.unit || ''\n\n // 공사기간은 기간을 일 단위로 계산\n if (projectKey === 'constructionPeriod') {\n planValue = calcDateDiff(this.project.startDate, this.project.endDate)\n } else if (!isDisplayInput) {\n planValue = 0\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 return html`<div class=\"row\">\n <div class=\"label\">• ${metric.name}</div>\n <div class=\"cell\">${isDisplayInput ? html`<input .value=${planValue} disabled /> ${unit}` : html`-`}</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"]}
|
|
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]\nconst EXCLUDE_FIELDS = [\n { id: 'b7a583c0-9de9-4c12-af49-dde65198dfce', name: '계획공사비' },\n { id: '9767747a-d2ec-4e36-96c4-4a78bba35b98', name: '실제공사비' },\n { id: '036d6e1c-193e-46bd-8d6e-93f248af8124', name: '계획공사기간' },\n { id: '5df77618-db44-4da3-a22d-43c067cb5e86', name: '실제공사기간' }\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 @state() kpiMetricValues: any = []\n @state() 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 // 제외 필드는 표시하지 않음\n if (EXCLUDE_FIELDS.some(item => item.id === metric.id)) return null\n\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 const isDisplayInput = projectKey === 'constructionPeriod' || projectKey === 'constructionCost' // 유효한 계획 필드\n\n // planValue는 project에서 찾고 없으면 buildingComplex의 값에서 찾음\n let planValue = this.project[projectKey] || this.project?.buildingComplex?.[projectKey] || 0\n const unit = kpiMetricValue.unit || metric.unit || ''\n\n // 공사기간은 기간을 일 단위로 계산\n if (projectKey === 'constructionPeriod') {\n planValue = Math.ceil(calcDateDiff(this.project.startDate, this.project.endDate) / 30)\n } else if (!isDisplayInput) {\n planValue = 0\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 return html`<div class=\"row\">\n <div class=\"label\">• ${metric.name}</div>\n <div class=\"cell\">${isDisplayInput ? html`<input .value=${planValue} disabled /> ${unit}` : html`-`}</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 _generateExcludeFieldsData() {\n const excludeData: any[] = []\n\n const constructionCostMetric = this.kpiMetrics.find(\n metric => KPI_METRIC_KEY_MAPPING.find(item => item.label === metric.name)?.projectKey === 'constructionCost'\n )\n\n // 공사기간 관련 메트릭을 찾음\n const constructionPeriodMetric = this.kpiMetrics.find(\n metric => KPI_METRIC_KEY_MAPPING.find(item => item.label === metric.name)?.projectKey === 'constructionPeriod'\n )\n\n // 실적공사비 값 (사용자가 입력한 constructionCost 값)\n const actualConstructionCostValue = constructionCostMetric\n ? this.kpiMetricValues.find((item: any) => item.metricId === constructionCostMetric.id)?.value || 0\n : 0\n\n // 실제공사기간 값 (사용자가 입력한 constructionPeriod 값)\n const actualConstructionPeriodValue = constructionPeriodMetric\n ? this.kpiMetricValues.find((item: any) => item.metricId === constructionPeriodMetric.id)?.value || 0\n : 0\n\n EXCLUDE_FIELDS.forEach(excludeField => {\n // 해당 excludeField.id에 매칭되는 메트릭 찾기\n const metric = this.kpiMetrics.find(m => m.id === excludeField.id)\n\n // 기존에 저장된 데이터가 있는지 확인\n const existingValue = this.kpiMetricValues.find((item: any) => item.metricId === excludeField.id)\n\n let value = 0\n if (excludeField.name == '계획공사비') {\n value = this.project?.buildingComplex?.constructionCost || 0\n } else if (excludeField.name == '실제공사비') {\n value = actualConstructionCostValue\n } else if (excludeField.name == '계획공사기간') {\n value = Math.ceil(calcDateDiff(this.project.startDate, this.project.endDate) / 30)\n } else if (excludeField.name == '실제공사기간') {\n value = actualConstructionPeriodValue\n }\n\n excludeData.push({\n id: existingValue?.id || crypto.randomUUID(), // 기존 데이터가 있으면 해당 id 사용\n value,\n metricId: excludeField.id,\n unit: metric?.unit || '',\n org: this.project.id,\n periodType: metric?.periodType,\n valueDate: moment().tz('Asia/Seoul').format('YYYY-MM-DD')\n })\n })\n\n return excludeData\n }\n\n private async _save() {\n // EXCLUDE_FIELDS에 대한 값들을 자동으로 생성\n const excludeData = this._generateExcludeFieldsData()\n\n // EXCLUDE_FIELDS에 해당하는 metricId를 추출\n const excludeMetricIds = EXCLUDE_FIELDS.map(field => field.id)\n // kpiMetricValues에서 EXCLUDE_FIELDS에 해당하는 중복 항목들을 제외\n const filteredKpiMetricValues = this.kpiMetricValues.filter((item: any) => !excludeMetricIds.includes(item.metricId))\n\n // 필터링된 데이터와 exclude 데이터를 합침\n const allKpiMetricValues = [...excludeData, ...filteredKpiMetricValues]\n\n console.log('저장할 데이터:', allKpiMetricValues)\n\n const response = await updateProjectCompleteStep1(allKpiMetricValues)\n if (!response.errors) {\n notify({ message: '저장되었습니다.' })\n }\n }\n\n private _reset() {\n this._getInitData()\n }\n}\n"]}
|
|
@@ -42,19 +42,16 @@ let SvProjectCompleteTab2Rating = class SvProjectCompleteTab2Rating extends LitE
|
|
|
42
42
|
<div class="stars" @mouseleave=${() => { }}>
|
|
43
43
|
${[1, 2, 3, 4, 5].map(starIndex => {
|
|
44
44
|
var _a;
|
|
45
|
-
const
|
|
46
|
-
const fullUntil = Math.floor(
|
|
47
|
-
const hasHalf =
|
|
48
|
-
const fillForThis = starIndex <= fullUntil ? 100 : starIndex === fullUntil + 1 && hasHalf ?
|
|
45
|
+
const score5 = Number((_a = kpiMetricValue === null || kpiMetricValue === void 0 ? void 0 : kpiMetricValue.value) !== null && _a !== void 0 ? _a : 0); // 0~5
|
|
46
|
+
const fullUntil = Math.floor(score5); // 정수 별 개수
|
|
47
|
+
const hasHalf = score5 % 1 === 0.5;
|
|
48
|
+
const fillForThis = starIndex <= fullUntil ? 100 : starIndex === fullUntil + 1 && hasHalf ? 50 : 0;
|
|
49
49
|
return html `
|
|
50
50
|
<span class="star-wrap">
|
|
51
51
|
<span class="star-base">☆</span>
|
|
52
52
|
<span class="star-fill" style="width: ${fillForThis}%;">★</span>
|
|
53
|
-
<span
|
|
54
|
-
|
|
55
|
-
@click=${() => this._setRatingHalf(metric.id, (starIndex - 1) * 2 + 1)}
|
|
56
|
-
></span>
|
|
57
|
-
<span class="click-half right" @click=${() => this._setRatingHalf(metric.id, starIndex * 2)}></span>
|
|
53
|
+
<span class="click-half left" @click=${() => this._setRatingHalf(metric.id, starIndex - 0.5)}></span>
|
|
54
|
+
<span class="click-half right" @click=${() => this._setRatingHalf(metric.id, starIndex)}></span>
|
|
58
55
|
</span>
|
|
59
56
|
`;
|
|
60
57
|
})}
|
|
@@ -91,13 +88,13 @@ let SvProjectCompleteTab2Rating = class SvProjectCompleteTab2Rating extends LitE
|
|
|
91
88
|
this.kpiMetrics = kpiMetrics.filter(item => item.name.includes('수준 평가')) || []; // 수준 평가 텍스트가 들어간 항목만
|
|
92
89
|
this.kpiMetricValues = await getKpiMetricValues(this.project.id);
|
|
93
90
|
}
|
|
94
|
-
_setRatingHalf(metricId,
|
|
95
|
-
//
|
|
91
|
+
_setRatingHalf(metricId, score5) {
|
|
92
|
+
// score5: 0~5, 0.5 단위
|
|
96
93
|
// 기존 배열에서 해당 metricId를 가진 항목을 찾음
|
|
97
94
|
const existingItemIndex = this.kpiMetricValues.findIndex((item) => item.metricId === metricId);
|
|
98
95
|
if (existingItemIndex !== -1) {
|
|
99
96
|
// 기존 항목이 있으면 업데이트
|
|
100
|
-
this.kpiMetricValues = this.kpiMetricValues.map((item) => item.metricId === metricId ? Object.assign(Object.assign({}, item), { value:
|
|
97
|
+
this.kpiMetricValues = this.kpiMetricValues.map((item) => item.metricId === metricId ? Object.assign(Object.assign({}, item), { value: score5 }) : item);
|
|
101
98
|
}
|
|
102
99
|
else {
|
|
103
100
|
// 기존 항목이 없으면 새로 추가
|
|
@@ -106,7 +103,7 @@ let SvProjectCompleteTab2Rating = class SvProjectCompleteTab2Rating extends LitE
|
|
|
106
103
|
...this.kpiMetricValues,
|
|
107
104
|
{
|
|
108
105
|
id: crypto.randomUUID(),
|
|
109
|
-
value:
|
|
106
|
+
value: score5,
|
|
110
107
|
metricId: metricId,
|
|
111
108
|
unit: (metric === null || metric === void 0 ? void 0 : metric.unit) || '',
|
|
112
109
|
org: this.project.id, // 프로젝트 ID 추가
|
|
@@ -1 +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, getKpiMetricValues, updateProjectCompleteStep2 } from '../../shared/complete-api'\nimport moment from 'moment-timezone'\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 .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\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: Object }) project: any = {}\n @state() kpiMetricValues: any = []\n @state() 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.kpiMetrics.map((metric, idx) => {\n const kpiMetricValue = this.kpiMetricValues.find((item: any) => item.metricId === metric.id) || {}\n const diff = calcDiff(0, kpiMetricValue?.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\">• ${metric.name}</div>\n <div class=\"cell\">${0}</div>\n <div class=\"stars\" @mouseleave=${() => {}}>\n ${[1, 2, 3, 4, 5].map(starIndex => {\n const score10 = Number(kpiMetricValue?.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\n class=\"click-half left\"\n @click=${() => this._setRatingHalf(metric.id, (starIndex - 1) * 2 + 1)}\n ></span>\n <span class=\"click-half right\" @click=${() => this._setRatingHalf(metric.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 if (this.project?.id) {\n this._getInitData()\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 private _setRatingHalf(metricId: string, score10: number) {\n // score10: 0~10, 0.5 단위\n\n // 기존 배열에서 해당 metricId를 가진 항목을 찾음\n const existingItemIndex = this.kpiMetricValues.findIndex((item: any) => item.metricId === metricId)\n\n if (existingItemIndex !== -1) {\n // 기존 항목이 있으면 업데이트\n this.kpiMetricValues = this.kpiMetricValues.map((item: any) =>\n item.metricId === metricId ? { ...item, value: score10 } : item\n )\n } else {\n // 기존 항목이 없으면 새로 추가\n const metric = this.kpiMetrics.find((m: any) => m.id === metricId)\n this.kpiMetricValues = [\n ...this.kpiMetricValues,\n {\n id: crypto.randomUUID(),\n value: score10,\n metricId: metricId,\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 this.dispatchEvent(new CustomEvent('complete-data-change', { detail: { tab: 2, data: this.kpiMetricValues } }))\n }\n\n private async _save() {\n const response = await updateProjectCompleteStep2(this.kpiMetricValues)\n if (!response.errors) {\n notify({ message: '저장되었습니다.' })\n }\n }\n\n private _reset() {\n this._getInitData()\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"pc-tab2-rating.js","sourceRoot":"","sources":["../../../client/pages/project-complete-tabs/pc-tab2-rating.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,KAAK,CAAA;AAC3C,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAClE,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC5C,OAAO,EAAoB,aAAa,EAAE,kBAAkB,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAA;AAC3H,OAAO,MAAM,MAAM,iBAAiB,CAAA;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAGjC,IAAM,2BAA2B,GAAjC,MAAM,2BAA4B,SAAQ,UAAU;IAApD;;QAsIuB,YAAO,GAAQ,EAAE,CAAA;QACpC,oBAAe,GAAQ,EAAE,CAAA;QACzB,eAAU,GAAQ,EAAE,CAAA;IA2H/B,CAAC;IAzHC,MAAM;QACJ,OAAO,IAAI,CAAA;;;;;;;;;;;;;;;;;UAiBL,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE;YACpC,MAAM,cAAc,GAAG,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,IAAS,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,KAAK,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,CAAA;YAClG,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,EAAE,cAAc,aAAd,cAAc,uBAAd,cAAc,CAAE,KAAK,CAAC,CAAA;YAC/C,MAAM,SAAS,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAA;YAC/D,MAAM,QAAQ,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAA;YAEvD,OAAO,IAAI,CAAA;;qCAEgB,MAAM,CAAC,IAAI;kCACd,CAAC;+CACY,GAAG,EAAE,GAAE,CAAC;kBACrC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE;;gBAChC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAA,cAAc,aAAd,cAAc,uBAAd,cAAc,CAAE,KAAK,mCAAI,CAAC,CAAC,CAAA,CAAC,MAAM;gBACxD,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA,CAAC,UAAU;gBAC/C,MAAM,OAAO,GAAG,MAAM,GAAG,CAAC,KAAK,GAAG,CAAA;gBAClC,MAAM,WAAW,GAAG,SAAS,IAAI,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,KAAK,SAAS,GAAG,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;gBAElG,OAAO,IAAI,CAAA;;;8DAGiC,WAAW;6DACZ,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC;8DACpD,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,CAAC;;mBAE1F,CAAA;YACH,CAAC,CAAC;;iCAEe,SAAS,KAAK,QAAQ,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,cAAc,EAAE;;WAE/E,CAAA;QACH,CAAC,CAAC;;;0CAGgC,IAAI,CAAC,MAAM;oDACD,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE;;;KAGjE,CAAA;IACH,CAAC;IAED,iBAAiB;;QACf,KAAK,CAAC,iBAAiB,EAAE,CAAA;QACzB,IAAI,MAAA,IAAI,CAAC,OAAO,0CAAE,EAAE,EAAE,CAAC;YACrB,IAAI,CAAC,YAAY,EAAE,CAAA;QACrB,CAAC;IACH,CAAC;IAED,UAAU,CAAC,iBAAmC;;QAC5C,KAAK,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAA;QAEnC,yCAAyC;QACzC,IAAI,iBAAiB,CAAC,GAAG,CAAC,SAAS,CAAC,KAAI,MAAA,IAAI,CAAC,OAAO,0CAAE,EAAE,CAAA,EAAE,CAAC;YACzD,IAAI,CAAC,YAAY,EAAE,CAAA;QACrB,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,YAAY;QACxB,MAAM,UAAU,GAAG,MAAM,aAAa,EAAE,CAAA;QACxC,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAA,CAAC,qBAAqB;QACpG,IAAI,CAAC,eAAe,GAAG,MAAM,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;IAClE,CAAC;IAEO,cAAc,CAAC,QAAgB,EAAE,MAAc;QACrD,sBAAsB;QAEtB,iCAAiC;QACjC,MAAM,iBAAiB,GAAG,IAAI,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC,IAAS,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAA;QAEnG,IAAI,iBAAiB,KAAK,CAAC,CAAC,EAAE,CAAC;YAC7B,kBAAkB;YAClB,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,IAAS,EAAE,EAAE,CAC5D,IAAI,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,iCAAM,IAAI,KAAE,KAAK,EAAE,MAAM,IAAG,CAAC,CAAC,IAAI,CAC/D,CAAA;QACH,CAAC;aAAM,CAAC;YACN,mBAAmB;YACnB,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAA;YAClE,IAAI,CAAC,eAAe,GAAG;gBACrB,GAAG,IAAI,CAAC,eAAe;gBACvB;oBACE,EAAE,EAAE,MAAM,CAAC,UAAU,EAAE;oBACvB,KAAK,EAAE,MAAM;oBACb,QAAQ,EAAE,QAAQ;oBAClB,IAAI,EAAE,CAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,IAAI,KAAI,EAAE;oBACxB,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,aAAa;oBACnC,UAAU,EAAE,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,UAAU;oBAC9B,SAAS,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC;iBAC1D;aACF,CAAA;QACH,CAAC;QAED,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,sBAAsB,EAAE,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,eAAe,EAAE,EAAE,CAAC,CAAC,CAAA;IACjH,CAAC;IAEO,KAAK,CAAC,KAAK;QACjB,MAAM,QAAQ,GAAG,MAAM,0BAA0B,CAAC,IAAI,CAAC,eAAe,CAAC,CAAA;QACvE,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;YACrB,MAAM,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAA;QACjC,CAAC;IACH,CAAC;IAEO,MAAM;QACZ,IAAI,CAAC,YAAY,EAAE,CAAA;IACrB,CAAC;;AAjQM,kCAAM,GAAG;IACd,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAiIF;CACF,AAnIY,CAmIZ;AAE2B;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;4DAAkB;AACpC;IAAR,KAAK,EAAE;;oEAA0B;AACzB;IAAR,KAAK,EAAE;;+DAAqB;AAxIlB,2BAA2B;IADvC,aAAa,CAAC,mBAAmB,CAAC;GACtB,2BAA2B,CAmQvC","sourcesContent":["import { css, html, LitElement } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { calcDiff } from '../../shared/func'\nimport { getKpiCategories, getKpiMetrics, getKpiMetricValues, updateProjectCompleteStep2 } from '../../shared/complete-api'\nimport moment from 'moment-timezone'\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 .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\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: Object }) project: any = {}\n @state() kpiMetricValues: any = []\n @state() 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.kpiMetrics.map((metric, idx) => {\n const kpiMetricValue = this.kpiMetricValues.find((item: any) => item.metricId === metric.id) || {}\n const diff = calcDiff(0, kpiMetricValue?.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\">• ${metric.name}</div>\n <div class=\"cell\">${0}</div>\n <div class=\"stars\" @mouseleave=${() => {}}>\n ${[1, 2, 3, 4, 5].map(starIndex => {\n const score5 = Number(kpiMetricValue?.value ?? 0) // 0~5\n const fullUntil = Math.floor(score5) // 정수 별 개수\n const hasHalf = score5 % 1 === 0.5\n const fillForThis = starIndex <= fullUntil ? 100 : starIndex === fullUntil + 1 && hasHalf ? 50 : 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(metric.id, starIndex - 0.5)}></span>\n <span class=\"click-half right\" @click=${() => this._setRatingHalf(metric.id, starIndex)}></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 if (this.project?.id) {\n this._getInitData()\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 private _setRatingHalf(metricId: string, score5: number) {\n // score5: 0~5, 0.5 단위\n\n // 기존 배열에서 해당 metricId를 가진 항목을 찾음\n const existingItemIndex = this.kpiMetricValues.findIndex((item: any) => item.metricId === metricId)\n\n if (existingItemIndex !== -1) {\n // 기존 항목이 있으면 업데이트\n this.kpiMetricValues = this.kpiMetricValues.map((item: any) =>\n item.metricId === metricId ? { ...item, value: score5 } : item\n )\n } else {\n // 기존 항목이 없으면 새로 추가\n const metric = this.kpiMetrics.find((m: any) => m.id === metricId)\n this.kpiMetricValues = [\n ...this.kpiMetricValues,\n {\n id: crypto.randomUUID(),\n value: score5,\n metricId: metricId,\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 this.dispatchEvent(new CustomEvent('complete-data-change', { detail: { tab: 2, data: this.kpiMetricValues } }))\n }\n\n private async _save() {\n const response = await updateProjectCompleteStep2(this.kpiMetricValues)\n if (!response.errors) {\n notify({ message: '저장되었습니다.' })\n }\n }\n\n private _reset() {\n this._getInitData()\n }\n}\n"]}
|