@dssp/dkpi 1.0.0-alpha.50 → 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.
- package/dist-client/components/kpi-boxplot-chart.d.ts +24 -0
- package/dist-client/components/kpi-boxplot-chart.js +328 -0
- package/dist-client/components/kpi-boxplot-chart.js.map +1 -0
- package/dist-client/components/kpi-radar-chart.d.ts +16 -0
- package/dist-client/components/kpi-radar-chart.js +139 -0
- package/dist-client/components/kpi-radar-chart.js.map +1 -0
- package/dist-client/pages/project-complete-tabs/pc-tab2-rating.d.ts +3 -1
- package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js +25 -12
- package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js.map +1 -1
- package/dist-client/pages/project-complete-tabs/pc-tab3-upload.d.ts +6 -3
- package/dist-client/pages/project-complete-tabs/pc-tab3-upload.js +101 -92
- package/dist-client/pages/project-complete-tabs/pc-tab3-upload.js.map +1 -1
- package/dist-client/pages/sv-project-complete.d.ts +0 -2
- package/dist-client/pages/sv-project-complete.js +14 -20
- package/dist-client/pages/sv-project-complete.js.map +1 -1
- package/dist-client/pages/sv-project-detail.d.ts +3 -0
- package/dist-client/pages/sv-project-detail.js +98 -34
- package/dist-client/pages/sv-project-detail.js.map +1 -1
- package/dist-client/shared/complete-api.d.ts +4 -1
- package/dist-client/shared/complete-api.js +68 -16
- package/dist-client/shared/complete-api.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.d.ts +2 -1
- package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js +28 -20
- 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 +3 -3
- package/schema.graphql +2 -2
|
@@ -2,11 +2,13 @@ import { __decorate, __metadata } from "tslib";
|
|
|
2
2
|
import { css, html, LitElement } from 'lit';
|
|
3
3
|
import { customElement, property } from 'lit/decorators.js';
|
|
4
4
|
import { calcDiff } from '../../shared/func';
|
|
5
|
+
import { getKpiCategories, getKpiMetrics, updateProjectCompleteStep2 } from '../../shared/complete-api';
|
|
6
|
+
import { notify } from '@operato/layout';
|
|
5
7
|
let SvProjectCompleteTab2Rating = class SvProjectCompleteTab2Rating extends LitElement {
|
|
6
8
|
constructor() {
|
|
7
9
|
super(...arguments);
|
|
8
|
-
this.data = [];
|
|
9
10
|
this.kpiCategories = [];
|
|
11
|
+
this.kpiMetrics = [];
|
|
10
12
|
}
|
|
11
13
|
render() {
|
|
12
14
|
return html `
|
|
@@ -27,7 +29,7 @@ let SvProjectCompleteTab2Rating = class SvProjectCompleteTab2Rating extends LitE
|
|
|
27
29
|
</div>
|
|
28
30
|
|
|
29
31
|
${this.kpiCategories.map((item, idx) => {
|
|
30
|
-
const completeScore = this.
|
|
32
|
+
const completeScore = this.kpiMetrics.find(v => v.id === item.id);
|
|
31
33
|
const diff = calcDiff(item.value, completeScore === null || completeScore === void 0 ? void 0 : completeScore.value);
|
|
32
34
|
const diffClass = diff === 0 ? '' : diff > 0 ? 'plus' : 'minus';
|
|
33
35
|
const diffSign = diff === 0 ? '' : diff > 0 ? '+' : '-';
|
|
@@ -41,7 +43,7 @@ let SvProjectCompleteTab2Rating = class SvProjectCompleteTab2Rating extends LitE
|
|
|
41
43
|
const score10 = Number((_a = completeScore === null || completeScore === void 0 ? void 0 : completeScore.value) !== null && _a !== void 0 ? _a : 0); // 0~10
|
|
42
44
|
const fullUntil = Math.floor(score10 / 2); // 정수 별 개수
|
|
43
45
|
const hasHalf = score10 % 2 === 1;
|
|
44
|
-
const fillForThis = starIndex <= fullUntil ? 100 : starIndex === fullUntil + 1 && hasHalf ?
|
|
46
|
+
const fillForThis = starIndex <= fullUntil ? 100 : starIndex === fullUntil + 1 && hasHalf ? 44 : 0;
|
|
45
47
|
return html `
|
|
46
48
|
<span class="star-wrap">
|
|
47
49
|
<span class="star-base">☆</span>
|
|
@@ -64,23 +66,34 @@ let SvProjectCompleteTab2Rating = class SvProjectCompleteTab2Rating extends LitE
|
|
|
64
66
|
</div>
|
|
65
67
|
`;
|
|
66
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
|
+
}
|
|
67
78
|
_setRatingHalf(itemId, score10) {
|
|
68
79
|
// score10: 0~10, 0.5 단위
|
|
69
|
-
const next = Array.isArray(this.
|
|
80
|
+
const next = Array.isArray(this.kpiMetrics) ? [...this.kpiMetrics] : [];
|
|
70
81
|
const idx = next.findIndex((v) => v.id === itemId);
|
|
71
82
|
if (idx >= 0)
|
|
72
83
|
next[idx] = Object.assign(Object.assign({}, next[idx]), { value: score10 });
|
|
73
84
|
else
|
|
74
85
|
next.push({ id: itemId, value: score10 });
|
|
75
|
-
this.
|
|
76
|
-
this.dispatchEvent(new CustomEvent('complete-data-change', { detail: { tab: 2, data: this.
|
|
86
|
+
this.kpiMetrics = next;
|
|
87
|
+
this.dispatchEvent(new CustomEvent('complete-data-change', { detail: { tab: 2, data: this.kpiMetrics } }));
|
|
77
88
|
}
|
|
78
|
-
_save() {
|
|
79
|
-
|
|
89
|
+
async _save() {
|
|
90
|
+
const response = await updateProjectCompleteStep2(this.kpiCategories);
|
|
91
|
+
if (!response.errors) {
|
|
92
|
+
notify({ message: '저장되었습니다.' });
|
|
93
|
+
}
|
|
80
94
|
}
|
|
81
95
|
_reset() {
|
|
82
|
-
this.
|
|
83
|
-
this.dispatchEvent(new CustomEvent('complete-tab-reset', { detail: { tab: 2 }, bubbles: true, composed: true }));
|
|
96
|
+
this._getInitData();
|
|
84
97
|
}
|
|
85
98
|
};
|
|
86
99
|
SvProjectCompleteTab2Rating.styles = [
|
|
@@ -217,11 +230,11 @@ SvProjectCompleteTab2Rating.styles = [
|
|
|
217
230
|
__decorate([
|
|
218
231
|
property({ type: Array }),
|
|
219
232
|
__metadata("design:type", Object)
|
|
220
|
-
], SvProjectCompleteTab2Rating.prototype, "
|
|
233
|
+
], SvProjectCompleteTab2Rating.prototype, "kpiCategories", void 0);
|
|
221
234
|
__decorate([
|
|
222
235
|
property({ type: Array }),
|
|
223
236
|
__metadata("design:type", Object)
|
|
224
|
-
], SvProjectCompleteTab2Rating.prototype, "
|
|
237
|
+
], SvProjectCompleteTab2Rating.prototype, "kpiMetrics", void 0);
|
|
225
238
|
SvProjectCompleteTab2Rating = __decorate([
|
|
226
239
|
customElement('sv-pc-tab2-rating')
|
|
227
240
|
], SvProjectCompleteTab2Rating);
|
|
@@ -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'\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 }) data: any = []\n @property({ type: Array }) kpiCategories: 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.data.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 ? 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(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 private _setRatingHalf(itemId: string, score10: number) {\n // score10: 0~10, 0.5 단위\n const next = Array.isArray(this.data) ? [...this.data] : []\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.data = next\n this.dispatchEvent(new CustomEvent('complete-data-change', { detail: { tab: 2, data: this.data } }))\n }\n\n private _save() {\n this.dispatchEvent(new CustomEvent('complete-tab-save', { detail: { data: this.data } }))\n }\n\n private _reset() {\n this.data = {}\n this.dispatchEvent(new CustomEvent('complete-tab-reset', { detail: { tab: 2 }, bubbles: true, composed: true }))\n }\n}\n"]}
|
|
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"]}
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { LitElement } from 'lit';
|
|
2
2
|
export declare class SvProjectCompleteTab3Upload extends LitElement {
|
|
3
3
|
static styles: import("lit").CSSResult[];
|
|
4
|
-
data: any;
|
|
5
4
|
project: any;
|
|
5
|
+
attachment: any;
|
|
6
|
+
private pendingFile;
|
|
6
7
|
render(): import("lit-html").TemplateResult<1>;
|
|
8
|
+
willUpdate(changedProperties: Map<string, any>): void;
|
|
9
|
+
private _getCurrentFile;
|
|
7
10
|
private _save;
|
|
11
|
+
private _removeFile;
|
|
8
12
|
private _reset;
|
|
9
13
|
private _onFileSelect;
|
|
10
|
-
private
|
|
11
|
-
private _deleteAttachment;
|
|
14
|
+
private _uploadFile;
|
|
12
15
|
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { __decorate, __metadata } from "tslib";
|
|
2
2
|
import { css, html, LitElement } from 'lit';
|
|
3
|
-
import { customElement, property } from 'lit/decorators.js';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { customElement, property, state } from 'lit/decorators.js';
|
|
4
|
+
import { getProject, updateProjectCompleteStep3 } from '../../shared/complete-api';
|
|
5
|
+
import { notify } from '@operato/layout';
|
|
6
6
|
let SvProjectCompleteTab3Upload = class SvProjectCompleteTab3Upload extends LitElement {
|
|
7
7
|
constructor() {
|
|
8
8
|
super(...arguments);
|
|
9
|
-
this.data = [];
|
|
10
9
|
this.project = {};
|
|
10
|
+
this.attachment = {};
|
|
11
|
+
this.pendingFile = null;
|
|
11
12
|
}
|
|
12
13
|
render() {
|
|
13
14
|
var _a, _b;
|
|
@@ -22,19 +23,21 @@ let SvProjectCompleteTab3Upload = class SvProjectCompleteTab3Upload extends LitE
|
|
|
22
23
|
</div>
|
|
23
24
|
|
|
24
25
|
<div class="rows">
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
26
|
+
${this._getCurrentFile()
|
|
27
|
+
? html `
|
|
28
|
+
<div class="attachment-list">
|
|
29
|
+
<div class="attachment-row">
|
|
30
|
+
<md-icon class="file-icon">picture_as_pdf</md-icon>
|
|
31
|
+
<div class="attachment-name" title="${(_a = this._getCurrentFile()) === null || _a === void 0 ? void 0 : _a.name}">${(_b = this._getCurrentFile()) === null || _b === void 0 ? void 0 : _b.name}</div>
|
|
32
|
+
<md-icon class="delete-icon" @click=${this._removeFile}>delete</md-icon>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
`
|
|
36
|
+
: html `
|
|
37
|
+
<div class="upload-controls">
|
|
38
|
+
<ox-input-file accept="application/pdf,.pdf" hide-filelist @change=${this._onFileSelect}> </ox-input-file>
|
|
39
|
+
</div>
|
|
40
|
+
`}
|
|
38
41
|
|
|
39
42
|
<div class="button-line">
|
|
40
43
|
<div class="ghost-btn " @click=${this._reset}>초기화</div>
|
|
@@ -43,62 +46,51 @@ let SvProjectCompleteTab3Upload = class SvProjectCompleteTab3Upload extends LitE
|
|
|
43
46
|
</div>
|
|
44
47
|
`;
|
|
45
48
|
}
|
|
46
|
-
|
|
47
|
-
this.dispatchEvent(new CustomEvent('complete-tab-save', { detail: { data: this.data } }));
|
|
48
|
-
}
|
|
49
|
-
async _reset() {
|
|
49
|
+
willUpdate(changedProperties) {
|
|
50
50
|
var _a;
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
super.willUpdate(changedProperties);
|
|
52
|
+
// project가 변경되고, project.id가 존재하면 attachment 설정
|
|
53
|
+
if (changedProperties.has('project') && ((_a = this.project) === null || _a === void 0 ? void 0 : _a.id)) {
|
|
54
|
+
this.attachment = this.project.completeReport;
|
|
55
|
+
}
|
|
53
56
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
+
_getCurrentFile() {
|
|
58
|
+
var _a;
|
|
59
|
+
return this.pendingFile || (((_a = this.attachment) === null || _a === void 0 ? void 0 : _a.id) ? this.attachment : null);
|
|
57
60
|
}
|
|
58
|
-
async
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
name
|
|
65
|
-
fullpath
|
|
66
|
-
}
|
|
61
|
+
async _save() {
|
|
62
|
+
var _a;
|
|
63
|
+
// 대기 중인 파일이 있으면 업로드
|
|
64
|
+
if (this.pendingFile) {
|
|
65
|
+
await this._uploadFile(this.pendingFile);
|
|
66
|
+
this.pendingFile = null;
|
|
67
67
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
attachments: files.map(file => {
|
|
71
|
-
var _a;
|
|
72
|
-
return ({
|
|
73
|
-
file,
|
|
74
|
-
refBy: (_a = this.project) === null || _a === void 0 ? void 0 : _a.id,
|
|
75
|
-
refType: 'ProjectCompleteReport'
|
|
76
|
-
});
|
|
77
|
-
})
|
|
78
|
-
},
|
|
79
|
-
context: {
|
|
80
|
-
hasUpload: true
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
if (!response.errors) {
|
|
84
|
-
const uploaded = response.data.createAttachments;
|
|
85
|
-
this.data = [...this.data, ...uploaded];
|
|
86
|
-
this.dispatchEvent(new CustomEvent('complete-data-change', { detail: { tab: 3, data: this.data } }));
|
|
68
|
+
else if (!this.pendingFile && ((_a = this.attachment) === null || _a === void 0 ? void 0 : _a.id)) {
|
|
69
|
+
notify({ message: '파일은 필수입니다.' });
|
|
87
70
|
}
|
|
88
71
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
72
|
+
_removeFile() {
|
|
73
|
+
this.pendingFile = null;
|
|
74
|
+
this.attachment = {};
|
|
75
|
+
}
|
|
76
|
+
// 대기 중인 파일, 저장된 파일 초기화
|
|
77
|
+
async _reset() {
|
|
78
|
+
this.pendingFile = null;
|
|
79
|
+
const project = await getProject(this.project.id);
|
|
80
|
+
this.attachment = project.completeReport;
|
|
81
|
+
}
|
|
82
|
+
async _onFileSelect(e) {
|
|
83
|
+
const files = e.detail;
|
|
84
|
+
if (files.length > 0) {
|
|
85
|
+
this.pendingFile = files[0];
|
|
95
86
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
87
|
+
}
|
|
88
|
+
async _uploadFile(file) {
|
|
89
|
+
const response = await updateProjectCompleteStep3(file, this.project.id);
|
|
99
90
|
if (!response.errors) {
|
|
100
|
-
|
|
101
|
-
this.
|
|
91
|
+
const uploaded = response.data.updateKpiMetricValuesSentiment;
|
|
92
|
+
this.attachment = uploaded;
|
|
93
|
+
notify({ message: '저장되었습니다.' });
|
|
102
94
|
}
|
|
103
95
|
}
|
|
104
96
|
};
|
|
@@ -136,45 +128,58 @@ SvProjectCompleteTab3Upload.styles = [
|
|
|
136
128
|
}
|
|
137
129
|
|
|
138
130
|
.attachment-list {
|
|
139
|
-
position: relative;
|
|
140
|
-
z-index: 1;
|
|
141
131
|
display: flex;
|
|
142
|
-
min-height: 80px;
|
|
143
132
|
justify-content: center;
|
|
144
|
-
flex-direction: column;
|
|
145
|
-
gap: 8px;
|
|
146
|
-
border: 1px solid #eee;
|
|
147
|
-
border-radius: 8px;
|
|
148
|
-
padding: 12px;
|
|
149
133
|
margin-top: 8px;
|
|
150
134
|
}
|
|
151
135
|
|
|
152
136
|
.attachment-row {
|
|
153
|
-
display:
|
|
154
|
-
|
|
137
|
+
display: flex;
|
|
138
|
+
flex-direction: column;
|
|
155
139
|
align-items: center;
|
|
156
|
-
gap:
|
|
157
|
-
padding:
|
|
158
|
-
border:
|
|
159
|
-
border-radius:
|
|
160
|
-
background: #
|
|
140
|
+
gap: 16px;
|
|
141
|
+
padding: 24px 20px;
|
|
142
|
+
border: 2px solid #e3f2fd;
|
|
143
|
+
border-radius: 12px;
|
|
144
|
+
background: linear-gradient(135deg, #f8fbff 0%, #e3f2fd 100%);
|
|
145
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
146
|
+
min-width: 300px;
|
|
147
|
+
max-width: 400px;
|
|
161
148
|
}
|
|
162
149
|
|
|
163
|
-
.
|
|
164
|
-
font-size:
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
150
|
+
.file-icon {
|
|
151
|
+
font-size: 40px;
|
|
152
|
+
color: #d32f2f;
|
|
153
|
+
line-height: 1;
|
|
154
|
+
display: flex;
|
|
155
|
+
align-items: center;
|
|
156
|
+
justify-content: center;
|
|
157
|
+
width: auto;
|
|
158
|
+
height: 48px;
|
|
168
159
|
}
|
|
169
160
|
|
|
170
|
-
.
|
|
161
|
+
.attachment-name {
|
|
162
|
+
font-size: 16px;
|
|
163
|
+
font-weight: 500;
|
|
164
|
+
color: #1976d2;
|
|
171
165
|
text-align: center;
|
|
172
|
-
|
|
173
|
-
|
|
166
|
+
word-break: break-word;
|
|
167
|
+
line-height: 1.4;
|
|
174
168
|
}
|
|
175
169
|
|
|
176
170
|
.delete-icon {
|
|
177
171
|
cursor: pointer;
|
|
172
|
+
color: #757575;
|
|
173
|
+
background: rgba(117, 117, 117, 0.1);
|
|
174
|
+
border-radius: 50%;
|
|
175
|
+
padding: 8px;
|
|
176
|
+
transition: all 0.2s ease;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.delete-icon:hover {
|
|
180
|
+
color: #e57373;
|
|
181
|
+
background: rgba(229, 115, 115, 0.15);
|
|
182
|
+
transform: scale(1.1);
|
|
178
183
|
}
|
|
179
184
|
|
|
180
185
|
.button-line {
|
|
@@ -198,14 +203,18 @@ SvProjectCompleteTab3Upload.styles = [
|
|
|
198
203
|
}
|
|
199
204
|
`
|
|
200
205
|
];
|
|
201
|
-
__decorate([
|
|
202
|
-
property({ type: Array }),
|
|
203
|
-
__metadata("design:type", Object)
|
|
204
|
-
], SvProjectCompleteTab3Upload.prototype, "data", void 0);
|
|
205
206
|
__decorate([
|
|
206
207
|
property({ type: Object }),
|
|
207
208
|
__metadata("design:type", Object)
|
|
208
209
|
], SvProjectCompleteTab3Upload.prototype, "project", void 0);
|
|
210
|
+
__decorate([
|
|
211
|
+
state(),
|
|
212
|
+
__metadata("design:type", Object)
|
|
213
|
+
], SvProjectCompleteTab3Upload.prototype, "attachment", void 0);
|
|
214
|
+
__decorate([
|
|
215
|
+
state(),
|
|
216
|
+
__metadata("design:type", Object)
|
|
217
|
+
], SvProjectCompleteTab3Upload.prototype, "pendingFile", void 0);
|
|
209
218
|
SvProjectCompleteTab3Upload = __decorate([
|
|
210
219
|
customElement('sv-pc-tab3-upload')
|
|
211
220
|
], SvProjectCompleteTab3Upload);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pc-tab3-upload.js","sourceRoot":"","sources":["../../../client/pages/project-complete-tabs/pc-tab3-upload.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,KAAK,CAAA;AAC3C,OAAO,EAAE,aAAa,EAAE,QAAQ,
|
|
1
|
+
{"version":3,"file":"pc-tab3-upload.js","sourceRoot":"","sources":["../../../client/pages/project-complete-tabs/pc-tab3-upload.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;AAGlE,OAAO,EAAE,UAAU,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAA;AAClF,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAGjC,IAAM,2BAA2B,GAAjC,MAAM,2BAA4B,SAAQ,UAAU;IAApD;;QA+GuB,YAAO,GAAQ,EAAE,CAAA;QACpC,eAAU,GAAQ,EAAE,CAAA;QACZ,gBAAW,GAAgB,IAAI,CAAA;IAwFlD,CAAC;IAtFC,MAAM;;QACJ,OAAO,IAAI,CAAA;;;;;;;;;;;UAWL,IAAI,CAAC,eAAe,EAAE;YACtB,CAAC,CAAC,IAAI,CAAA;;;;wDAIwC,MAAA,IAAI,CAAC,eAAe,EAAE,0CAAE,IAAI,KAAK,MAAA,IAAI,CAAC,eAAe,EAAE,0CAAE,IAAI;wDAC7D,IAAI,CAAC,WAAW;;;aAG3D;YACH,CAAC,CAAC,IAAI,CAAA;;qFAEqE,IAAI,CAAC,aAAa;;aAE1F;;;2CAG8B,IAAI,CAAC,MAAM;oDACF,GAAG,EAAE,CAAC,IAAI,CAAC,KAAK,EAAE;;;KAGjE,CAAA;IACH,CAAC;IAED,UAAU,CAAC,iBAAmC;;QAC5C,KAAK,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAA;QAEnC,gDAAgD;QAChD,IAAI,iBAAiB,CAAC,GAAG,CAAC,SAAS,CAAC,KAAI,MAAA,IAAI,CAAC,OAAO,0CAAE,EAAE,CAAA,EAAE,CAAC;YACzD,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,CAAA;QAC/C,CAAC;IACH,CAAC;IAEO,eAAe;;QACrB,OAAO,IAAI,CAAC,WAAW,IAAI,CAAC,CAAA,MAAA,IAAI,CAAC,UAAU,0CAAE,EAAE,EAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;IAC3E,CAAC;IAEO,KAAK,CAAC,KAAK;;QACjB,oBAAoB;QACpB,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;YACxC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAA;QACzB,CAAC;aAAM,IAAI,CAAC,IAAI,CAAC,WAAW,KAAI,MAAA,IAAI,CAAC,UAAU,0CAAE,EAAE,CAAA,EAAE,CAAC;YACpD,MAAM,CAAC,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAA;QACnC,CAAC;IACH,CAAC;IAEO,WAAW;QACjB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAA;QACvB,IAAI,CAAC,UAAU,GAAG,EAAE,CAAA;IACtB,CAAC;IAED,uBAAuB;IACf,KAAK,CAAC,MAAM;QAClB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAA;QACvB,MAAM,OAAO,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;QACjD,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,cAAc,CAAA;IAC1C,CAAC;IAEO,KAAK,CAAC,aAAa,CAAC,CAAc;QACxC,MAAM,KAAK,GAAG,CAAC,CAAC,MAAgB,CAAA;QAChC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACrB,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,CAAC,CAAC,CAAA;QAC7B,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,WAAW,CAAC,IAAiB;QACzC,MAAM,QAAQ,GAAG,MAAM,0BAA0B,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;QACxE,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;YACrB,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,8BAA8B,CAAA;YAC7D,IAAI,CAAC,UAAU,GAAG,QAAQ,CAAA;YAC1B,MAAM,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAA;QACjC,CAAC;IACH,CAAC;;AAvMM,kCAAM,GAAG;IACd,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA0GF;CACF,AA5GY,CA4GZ;AAE2B;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;4DAAkB;AACpC;IAAR,KAAK,EAAE;;+DAAqB;AACZ;IAAhB,KAAK,EAAE;;gEAAwC;AAjHrC,2BAA2B;IADvC,aAAa,CAAC,mBAAmB,CAAC;GACtB,2BAA2B,CAyMvC","sourcesContent":["import { css, html, LitElement } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { client } from '@operato/graphql'\nimport { gql } from '@apollo/client'\nimport { getProject, updateProjectCompleteStep3 } from '../../shared/complete-api'\nimport { notify } from '@operato/layout'\n\n@customElement('sv-pc-tab3-upload')\nexport class SvProjectCompleteTab3Upload 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 gap: 12px;\n padding: 8px 6px;\n }\n\n .upload-controls {\n display: flex;\n gap: 8px;\n justify-content: center;\n margin-bottom: 16px;\n\n ox-input-file {\n flex: 1;\n max-width: 500px;\n height: 210px;\n }\n }\n\n .attachment-list {\n display: flex;\n justify-content: center;\n margin-top: 8px;\n }\n\n .attachment-row {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 16px;\n padding: 24px 20px;\n border: 2px solid #e3f2fd;\n border-radius: 12px;\n background: linear-gradient(135deg, #f8fbff 0%, #e3f2fd 100%);\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n min-width: 300px;\n max-width: 400px;\n }\n\n .file-icon {\n font-size: 40px;\n color: #d32f2f;\n line-height: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n width: auto;\n height: 48px;\n }\n\n .attachment-name {\n font-size: 16px;\n font-weight: 500;\n color: #1976d2;\n text-align: center;\n word-break: break-word;\n line-height: 1.4;\n }\n\n .delete-icon {\n cursor: pointer;\n color: #757575;\n background: rgba(117, 117, 117, 0.1);\n border-radius: 50%;\n padding: 8px;\n transition: all 0.2s ease;\n }\n\n .delete-icon:hover {\n color: #e57373;\n background: rgba(229, 115, 115, 0.15);\n transform: scale(1.1);\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() attachment: any = {}\n @state() private pendingFile: File | null = null\n\n render() {\n return html`\n <div class=\"title\">\n <div>\n 감리 최종 감리보고서의 종합의견서(PDF 형식)를 시스템에 업로드해 주세요.\n <br />\n 보고서에 포함된 텍스트와 평가 내용을 기반으로 기계 판독 및 AI 기반 프로젝트 평가가 자동으로 진행되므로, 반드시 최종\n 확정된 원본 파일을 제출해 주시기 바랍니다.\n </div>\n </div>\n\n <div class=\"rows\">\n ${this._getCurrentFile()\n ? html`\n <div class=\"attachment-list\">\n <div class=\"attachment-row\">\n <md-icon class=\"file-icon\">picture_as_pdf</md-icon>\n <div class=\"attachment-name\" title=\"${this._getCurrentFile()?.name}\">${this._getCurrentFile()?.name}</div>\n <md-icon class=\"delete-icon\" @click=${this._removeFile}>delete</md-icon>\n </div>\n </div>\n `\n : html`\n <div class=\"upload-controls\">\n <ox-input-file accept=\"application/pdf,.pdf\" hide-filelist @change=${this._onFileSelect}> </ox-input-file>\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가 존재하면 attachment 설정\n if (changedProperties.has('project') && this.project?.id) {\n this.attachment = this.project.completeReport\n }\n }\n\n private _getCurrentFile() {\n return this.pendingFile || (this.attachment?.id ? this.attachment : null)\n }\n\n private async _save() {\n // 대기 중인 파일이 있으면 업로드\n if (this.pendingFile) {\n await this._uploadFile(this.pendingFile)\n this.pendingFile = null\n } else if (!this.pendingFile && this.attachment?.id) {\n notify({ message: '파일은 필수입니다.' })\n }\n }\n\n private _removeFile() {\n this.pendingFile = null\n this.attachment = {}\n }\n\n // 대기 중인 파일, 저장된 파일 초기화\n private async _reset() {\n this.pendingFile = null\n const project = await getProject(this.project.id)\n this.attachment = project.completeReport\n }\n\n private async _onFileSelect(e: CustomEvent) {\n const files = e.detail as File[]\n if (files.length > 0) {\n this.pendingFile = files[0]\n }\n }\n\n private async _uploadFile(file: File | null) {\n const response = await updateProjectCompleteStep3(file, this.project.id)\n if (!response.errors) {\n const uploaded = response.data.updateKpiMetricValuesSentiment\n this.attachment = uploaded\n notify({ message: '저장되었습니다.' })\n }\n }\n}\n"]}
|
|
@@ -12,10 +12,8 @@ export declare class SvProjectCompletePage extends SvProjectCompletePage_base {
|
|
|
12
12
|
private activeTab;
|
|
13
13
|
private projectId;
|
|
14
14
|
private project;
|
|
15
|
-
private kpiCategories;
|
|
16
15
|
render(): import("lit-html").TemplateResult<1>;
|
|
17
16
|
pageUpdated(changes: any, lifecycle: any): Promise<void>;
|
|
18
|
-
initProject(projectId?: string): Promise<void>;
|
|
19
17
|
private _onTabClick;
|
|
20
18
|
private _onComplete;
|
|
21
19
|
}
|
|
@@ -7,26 +7,30 @@ import { ScopedElementsMixin } from '@open-wc/scoped-elements';
|
|
|
7
7
|
import './project-complete-tabs/pc-tab1-plan';
|
|
8
8
|
import './project-complete-tabs/pc-tab2-rating';
|
|
9
9
|
import './project-complete-tabs/pc-tab3-upload';
|
|
10
|
-
import { getProject,
|
|
10
|
+
import { getProject, updateProjectCompleteFinalize } from '../shared/complete-api';
|
|
11
11
|
import { notify } from '@operato/layout';
|
|
12
|
+
import { OxPrompt } from '@operato/popup';
|
|
12
13
|
let SvProjectCompletePage = class SvProjectCompletePage extends ScopedElementsMixin(PageView) {
|
|
13
14
|
constructor() {
|
|
14
15
|
super(...arguments);
|
|
15
16
|
this.activeTab = 1;
|
|
16
17
|
this.projectId = '';
|
|
17
18
|
this.project = {};
|
|
18
|
-
|
|
19
|
+
// 탭 클릭
|
|
19
20
|
this._onTabClick = async (tabNumber) => {
|
|
20
21
|
if (tabNumber === this.activeTab)
|
|
21
22
|
return;
|
|
22
23
|
this.activeTab = tabNumber;
|
|
23
24
|
};
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
// 완공 처리
|
|
26
|
+
this._onComplete = async () => {
|
|
27
|
+
if (await OxPrompt.open({ title: '완공 처리를 하시겠습니까?', confirmButton: { text: '확인' }, cancelButton: { text: '취소' } })) {
|
|
28
|
+
const result = await updateProjectCompleteFinalize(this.projectId);
|
|
29
|
+
if (!result.errors) {
|
|
30
|
+
notify({ message: '완공 처리가 완료되었습니다.', level: 'info' });
|
|
31
|
+
navigate(`project-detail/${this.projectId}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
30
34
|
};
|
|
31
35
|
}
|
|
32
36
|
get context() {
|
|
@@ -63,7 +67,7 @@ let SvProjectCompletePage = class SvProjectCompletePage extends ScopedElementsMi
|
|
|
63
67
|
${this.activeTab === 1
|
|
64
68
|
? html `<sv-pc-tab1-plan .project=${this.project}></sv-pc-tab1-plan>`
|
|
65
69
|
: this.activeTab === 2
|
|
66
|
-
? html `<sv-pc-tab2-rating
|
|
70
|
+
? html `<sv-pc-tab2-rating></sv-pc-tab2-rating>`
|
|
67
71
|
: html `<sv-pc-tab3-upload .project=${this.project}></sv-pc-tab3-upload>`}
|
|
68
72
|
</div>
|
|
69
73
|
</div>
|
|
@@ -72,15 +76,9 @@ let SvProjectCompletePage = class SvProjectCompletePage extends ScopedElementsMi
|
|
|
72
76
|
async pageUpdated(changes, lifecycle) {
|
|
73
77
|
if (this.active) {
|
|
74
78
|
this.projectId = lifecycle.resourceId || '';
|
|
75
|
-
await
|
|
79
|
+
this.project = await getProject(this.projectId); // View용 데이터
|
|
76
80
|
}
|
|
77
81
|
}
|
|
78
|
-
async initProject(projectId = '') {
|
|
79
|
-
const project = await getProject(projectId);
|
|
80
|
-
const kpiCategories = await getKpiCategories();
|
|
81
|
-
this.project = project; // View용 데이터
|
|
82
|
-
this.kpiCategories = kpiCategories; // View용 데이터
|
|
83
|
-
}
|
|
84
82
|
};
|
|
85
83
|
SvProjectCompletePage.styles = [
|
|
86
84
|
css `
|
|
@@ -202,10 +200,6 @@ __decorate([
|
|
|
202
200
|
state(),
|
|
203
201
|
__metadata("design:type", Object)
|
|
204
202
|
], SvProjectCompletePage.prototype, "project", void 0);
|
|
205
|
-
__decorate([
|
|
206
|
-
state(),
|
|
207
|
-
__metadata("design:type", Array)
|
|
208
|
-
], SvProjectCompletePage.prototype, "kpiCategories", void 0);
|
|
209
203
|
SvProjectCompletePage = __decorate([
|
|
210
204
|
customElement('sv-project-complete')
|
|
211
205
|
], SvProjectCompletePage);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sv-project-complete.js","sourceRoot":"","sources":["../../client/pages/sv-project-complete.ts"],"names":[],"mappings":";AAAA,OAAO,4BAA4B,CAAA;AAEnC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACnD,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAA;AAC/B,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAE9D,OAAO,sCAAsC,CAAA;AAC7C,OAAO,wCAAwC,CAAA;AAC/C,OAAO,wCAAwC,CAAA;AAC/C,OAAO,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"sv-project-complete.js","sourceRoot":"","sources":["../../client/pages/sv-project-complete.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\n\nimport { navigate, PageView } from '@operato/shell'\nimport { css, html } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\nimport { ScopedElementsMixin } from '@open-wc/scoped-elements'\n\nimport './project-complete-tabs/pc-tab1-plan'\nimport './project-complete-tabs/pc-tab2-rating'\nimport './project-complete-tabs/pc-tab3-upload'\nimport { getProject, getKpiCategories, updateProjectCompleteFinalize } from '../shared/complete-api'\nimport { notify } from '@operato/layout'\nimport { OxPrompt } from '@operato/popup'\n\n@customElement('sv-project-complete')\nexport class SvProjectCompletePage extends ScopedElementsMixin(PageView) {\n static styles = [\n css`\n :host {\n display: flex;\n flex-direction: column;\n overflow-y: auto;\n\n width: 100%;\n height: 100%;\n background-color: var(--md-sys-color-background, #f6f6f6);\n }\n\n .page-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 16px 20px 8px 20px;\n }\n .page-title {\n color: #35618e;\n font-weight: 700;\n font-size: 22px;\n letter-spacing: -0.05em;\n }\n .triangle {\n width: 0;\n height: 0;\n border-left: 6px solid transparent;\n border-right: 6px solid transparent;\n border-top: 8px solid #35618e;\n }\n\n .card {\n background: #ffffff;\n border-radius: 8px;\n padding: 12px;\n box-shadow: 2px 2px 2px 0 rgba(0, 0, 0, 0.08);\n margin: 0 20px 20px 20px;\n }\n\n .tabs {\n display: flex;\n justify-content: center;\n padding: 0 12px 8px 12px;\n border-bottom: 1px dashed rgba(0, 0, 0, 0.15);\n }\n .tab {\n display: inline-flex;\n align-items: center;\n padding: 6px 14px;\n border: 1px solid rgba(0, 0, 0, 0.12);\n color: #35618e;\n background: #f3f6f9;\n cursor: pointer;\n font-size: 14px;\n letter-spacing: -0.02em;\n }\n .tab:first-child {\n border-top-left-radius: 10px;\n border-bottom-left-radius: 10px;\n }\n .tab:last-child {\n border-top-right-radius: 10px;\n border-bottom-right-radius: 10px;\n }\n /* 중간 탭 이중 보더 방지: 좌측 보더 제거 */\n .tab + .tab {\n border-left: 0;\n }\n .tab[active] {\n background: #02a8a2;\n color: #ffffff;\n border-color: rgba(148, 163, 184, 0.5);\n font-weight: 700;\n }\n\n .tab-body {\n padding: 12px 8px 4px 8px;\n }\n\n .button-line {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin: 0 20px 10px 20px;\n }\n .spacer {\n flex: 1;\n }\n .ghost-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 7px 12px;\n background: #35618e;\n color: #ffffff;\n border-radius: 5px;\n box-shadow: 2px 2px 2px 0 rgba(0, 0, 0, 0.1);\n cursor: pointer;\n }\n .ghost-btn.secondary {\n background: #3395f1;\n }\n .ghost-btn.complete {\n background: #16a085;\n }\n `\n ]\n\n get context() {\n return {\n title: '프로젝트 완공 처리'\n }\n }\n\n @state() private activeTab: number = 1\n @state() private projectId: string = ''\n @state() private project: any = {}\n\n render() {\n return html`\n <div class=\"page-header\">\n <div class=\"page-title\">프로젝트 완공 처리</div>\n <div class=\"triangle\"></div>\n <span class=\"spacer\"></span>\n <div class=\"ghost-btn\" @click=${() => navigate(`project-detail/${this.projectId}`)}>\n <md-icon>arrow_back</md-icon>\n <div>상세로 돌아가기</div>\n </div>\n <div class=\"ghost-btn complete\" @click=${this._onComplete}>\n <md-icon>check_circle</md-icon>\n <div>완공 처리</div>\n </div>\n </div>\n\n <div class=\"card\">\n <div class=\"tabs\">\n <div class=\"tab\" ?active=${this.activeTab === 1} @click=${() => this._onTabClick(1)}>\n Step1. 프로젝트 기본정보 현행화\n </div>\n <div class=\"tab\" ?active=${this.activeTab === 2} @click=${() => this._onTabClick(2)}>Step2. 프로젝트 완료 평가</div>\n <div class=\"tab\" ?active=${this.activeTab === 3} @click=${() => this._onTabClick(3)}>Step3. 준공 문서 업로드</div>\n </div>\n\n <div class=\"tab-body\">\n ${this.activeTab === 1\n ? html`<sv-pc-tab1-plan .project=${this.project}></sv-pc-tab1-plan>`\n : this.activeTab === 2\n ? html`<sv-pc-tab2-rating></sv-pc-tab2-rating>`\n : html`<sv-pc-tab3-upload .project=${this.project}></sv-pc-tab3-upload>`}\n </div>\n </div>\n `\n }\n\n async pageUpdated(changes: any, lifecycle: any) {\n if (this.active) {\n this.projectId = lifecycle.resourceId || ''\n\n this.project = await getProject(this.projectId) // View용 데이터\n }\n }\n\n // 탭 클릭\n private _onTabClick = async (tabNumber: number) => {\n if (tabNumber === this.activeTab) return\n this.activeTab = tabNumber\n }\n\n // 완공 처리\n private _onComplete = async () => {\n if (\n await OxPrompt.open({ title: '완공 처리를 하시겠습니까?', confirmButton: { text: '확인' }, cancelButton: { text: '취소' } })\n ) {\n const result = await updateProjectCompleteFinalize(this.projectId)\n if (!result.errors) {\n notify({ message: '완공 처리가 완료되었습니다.', level: 'info' })\n navigate(`project-detail/${this.projectId}`)\n }\n }\n }\n}\n"]}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { PageLifecycle, PageView } from '@operato/shell';
|
|
2
|
+
import '../components/kpi-boxplot-chart';
|
|
3
|
+
import '../components/kpi-single-boxplot-chart';
|
|
4
|
+
import '../components/kpi-radar-chart';
|
|
2
5
|
declare const SvProjectDetailPage_base: typeof PageView & import("@open-wc/dedupe-mixin").Constructor<import("@open-wc/scoped-elements/types/src/types").ScopedElementsHost>;
|
|
3
6
|
export declare class SvProjectDetailPage extends SvProjectDetailPage_base {
|
|
4
7
|
static styles: import("lit").CSSResult[];
|