@things-factory/kpi 10.0.0-beta.6 → 10.0.0-beta.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/client/index.ts +1 -0
- package/client/pages/kpi/kpi-list-page.ts +150 -17
- package/client/pages/kpi-dashboard/components/kpi-map-panel.ts +2 -2
- package/client/pages/kpi-metric/kpi-metric-list-page.ts +17 -3
- package/dist-client/index.d.ts +1 -0
- package/dist-client/index.js +1 -1
- package/dist-client/index.js.map +1 -1
- package/dist-client/pages/kpi/kpi-list-page.d.ts +48 -3
- package/dist-client/pages/kpi/kpi-list-page.js +140 -18
- package/dist-client/pages/kpi/kpi-list-page.js.map +1 -1
- package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js +2 -2
- package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js.map +1 -1
- package/dist-client/pages/kpi-metric/kpi-metric-list-page.js +17 -3
- package/dist-client/pages/kpi-metric/kpi-metric-list-page.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/service/kpi/kpi-type.d.ts +5 -1
- package/dist-server/service/kpi/kpi-type.js +28 -0
- package/dist-server/service/kpi/kpi-type.js.map +1 -1
- package/dist-server/service/kpi/kpi.d.ts +64 -0
- package/dist-server/service/kpi/kpi.js +89 -1
- package/dist-server/service/kpi/kpi.js.map +1 -1
- package/dist-server/service/kpi-metric/kpi-metric-mutation.js +15 -7
- package/dist-server/service/kpi-metric/kpi-metric-mutation.js.map +1 -1
- package/dist-server/service/kpi-metric/kpi-metric-query.d.ts +2 -0
- package/dist-server/service/kpi-metric/kpi-metric-query.js +13 -1
- package/dist-server/service/kpi-metric/kpi-metric-query.js.map +1 -1
- package/dist-server/service/kpi-metric/kpi-metric-type.d.ts +3 -2
- package/dist-server/service/kpi-metric/kpi-metric-type.js +7 -6
- package/dist-server/service/kpi-metric/kpi-metric-type.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
package/client/index.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { KpiListPage } from './pages/kpi/kpi-list-page.js'
|
|
@@ -21,6 +21,44 @@ import { p13n } from '@operato/p13n'
|
|
|
21
21
|
|
|
22
22
|
import gql from 'graphql-tag'
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* KPI scoreType — value → score 변환 방식 (null이면 미설정)
|
|
26
|
+
*
|
|
27
|
+
* DIRECT — value = score, 변환 없음
|
|
28
|
+
* FORMULA — scoreFormula 수식으로 변환
|
|
29
|
+
* LOOKUP — grade table(1D)로 변환
|
|
30
|
+
* CUSTOM — 2D 룩업 등 특수 변환
|
|
31
|
+
*/
|
|
32
|
+
export const KPI_SCORE_TYPE = {
|
|
33
|
+
DIRECT: 'DIRECT',
|
|
34
|
+
FORMULA: 'FORMULA',
|
|
35
|
+
LOOKUP: 'LOOKUP',
|
|
36
|
+
CUSTOM: 'CUSTOM'
|
|
37
|
+
} as const
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* KPI valueType — value 획득 방식
|
|
41
|
+
*
|
|
42
|
+
* MEASURED — 외부 시스템 수집 측정값
|
|
43
|
+
* ASSESSED — 감리자 직접 평가 (1~5)
|
|
44
|
+
* CALCULATED — formula 자동 계산
|
|
45
|
+
* COMPOSITE — 다차원 복합 입력
|
|
46
|
+
*/
|
|
47
|
+
export const KPI_VALUE_TYPE = {
|
|
48
|
+
MEASURED: 'MEASURED',
|
|
49
|
+
ASSESSED: 'ASSESSED',
|
|
50
|
+
CALCULATED: 'CALCULATED',
|
|
51
|
+
COMPOSITE: 'COMPOSITE'
|
|
52
|
+
} as const
|
|
53
|
+
|
|
54
|
+
export function inferScoreType(kpi: any): string {
|
|
55
|
+
if (kpi.scoreType) return kpi.scoreType
|
|
56
|
+
if (kpi.scoreFormula) return KPI_SCORE_TYPE.FORMULA
|
|
57
|
+
if (kpi.grades && typeof kpi.grades === 'object' && !Array.isArray(kpi.grades)) return KPI_SCORE_TYPE.CUSTOM
|
|
58
|
+
if (Array.isArray(kpi.grades) && kpi.grades.length > 0) return KPI_SCORE_TYPE.LOOKUP
|
|
59
|
+
return KPI_SCORE_TYPE.DIRECT
|
|
60
|
+
}
|
|
61
|
+
|
|
24
62
|
import { KpiImporter } from './kpi-importer'
|
|
25
63
|
import { KpiGradeEditor } from './kpi-grade-editor'
|
|
26
64
|
import { KpiVizEditor } from './kpi-viz-editor'
|
|
@@ -57,7 +95,7 @@ export class KpiListPage extends p13n(localize(i18next)(ScopedElementsMixin(Page
|
|
|
57
95
|
@property({ type: Object }) gristConfig: any
|
|
58
96
|
@property({ type: String }) mode: 'CARD' | 'GRID' | 'LIST' = isMobileDevice() ? 'CARD' : 'GRID'
|
|
59
97
|
|
|
60
|
-
@query('ox-grist')
|
|
98
|
+
@query('ox-grist') protected grist!: DataGrist
|
|
61
99
|
|
|
62
100
|
@state() availableVariables: any[] = []
|
|
63
101
|
@state() availableVariablesLoaded = false
|
|
@@ -387,17 +425,42 @@ export class KpiListPage extends p13n(localize(i18next)(ScopedElementsMixin(Page
|
|
|
387
425
|
name: 'grades',
|
|
388
426
|
header: '성과지수 Lookup',
|
|
389
427
|
record: { editable: false,
|
|
390
|
-
renderer: (v, c, r) =>
|
|
391
|
-
${r.grades.length}개 등급 설정됨
|
|
392
|
-
</span>`
|
|
393
|
-
} else { return html`<span style="color: #999; cursor: pointer;" @click=${() => this._editGrades(r)}>
|
|
394
|
-
등급 설정 없음
|
|
395
|
-
</span>`
|
|
396
|
-
}
|
|
397
|
-
}
|
|
428
|
+
renderer: (v, c, r) => this.renderGradesCell(r)
|
|
398
429
|
},
|
|
399
430
|
width: 150
|
|
400
431
|
},
|
|
432
|
+
{
|
|
433
|
+
type: 'select',
|
|
434
|
+
name: 'scoreType',
|
|
435
|
+
header: 'Score 산정',
|
|
436
|
+
record: {
|
|
437
|
+
editable: true,
|
|
438
|
+
options: [
|
|
439
|
+
{ value: '', display: '(미설정)' },
|
|
440
|
+
{ value: 'DIRECT', display: 'DIRECT (변환없음)' },
|
|
441
|
+
{ value: 'FORMULA', display: 'FORMULA (수식)' },
|
|
442
|
+
{ value: 'LOOKUP', display: 'LOOKUP (등급표)' },
|
|
443
|
+
{ value: 'CUSTOM', display: 'CUSTOM (특수)' }
|
|
444
|
+
]
|
|
445
|
+
},
|
|
446
|
+
width: 130
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
type: 'select',
|
|
450
|
+
name: 'valueType',
|
|
451
|
+
header: 'Value 획득',
|
|
452
|
+
record: {
|
|
453
|
+
editable: true,
|
|
454
|
+
options: [
|
|
455
|
+
{ value: '', display: '(미설정)' },
|
|
456
|
+
{ value: 'MEASURED', display: 'MEASURED (외부수집)' },
|
|
457
|
+
{ value: 'ASSESSED', display: 'ASSESSED (감리자평가)' },
|
|
458
|
+
{ value: 'CALCULATED', display: 'CALCULATED (산식)' },
|
|
459
|
+
{ value: 'COMPOSITE', display: 'COMPOSITE (복합)' }
|
|
460
|
+
]
|
|
461
|
+
},
|
|
462
|
+
width: 140
|
|
463
|
+
},
|
|
401
464
|
{ type: 'number',
|
|
402
465
|
name: 'weight',
|
|
403
466
|
header: '가중치',
|
|
@@ -487,6 +550,8 @@ export class KpiListPage extends p13n(localize(i18next)(ScopedElementsMixin(Page
|
|
|
487
550
|
active
|
|
488
551
|
formula
|
|
489
552
|
periodType
|
|
553
|
+
scoreType
|
|
554
|
+
valueType
|
|
490
555
|
scoreFormula
|
|
491
556
|
grades
|
|
492
557
|
vizType
|
|
@@ -654,19 +719,86 @@ export class KpiListPage extends p13n(localize(i18next)(ScopedElementsMixin(Page
|
|
|
654
719
|
}
|
|
655
720
|
}
|
|
656
721
|
|
|
657
|
-
|
|
658
|
-
|
|
722
|
+
/**
|
|
723
|
+
* grades 셀 렌더러. 서브클래스에서 override 가능.
|
|
724
|
+
* CUSTOM scoreType은 renderCustomGradesCell()로 위임.
|
|
725
|
+
*/
|
|
726
|
+
protected renderGradesCell(kpi: any): any {
|
|
727
|
+
const scoreType = inferScoreType(kpi)
|
|
728
|
+
let label: string
|
|
729
|
+
let hasGrades: boolean
|
|
730
|
+
|
|
731
|
+
switch (scoreType) {
|
|
732
|
+
case KPI_SCORE_TYPE.LOOKUP:
|
|
733
|
+
hasGrades = Array.isArray(kpi.grades) && kpi.grades.length > 0
|
|
734
|
+
label = hasGrades ? `${kpi.grades.length}개 등급 설정됨` : '등급 설정 없음'
|
|
735
|
+
break
|
|
736
|
+
case KPI_SCORE_TYPE.DIRECT:
|
|
737
|
+
// valueType으로 세분화 표시
|
|
738
|
+
if (kpi.valueType === KPI_VALUE_TYPE.ASSESSED) {
|
|
739
|
+
return html`<span style="color:#2196f3">평가형 (1~5)</span>`
|
|
740
|
+
}
|
|
741
|
+
if (kpi.valueType === KPI_VALUE_TYPE.CALCULATED) {
|
|
742
|
+
return html`<span style="color:#888">산식 계산</span>`
|
|
743
|
+
}
|
|
744
|
+
return html`<span style="color:#888">직접 (value=score)</span>`
|
|
745
|
+
case KPI_SCORE_TYPE.FORMULA:
|
|
746
|
+
return html`<span style="color:#888">산식 변환</span>`
|
|
747
|
+
case KPI_SCORE_TYPE.CUSTOM:
|
|
748
|
+
return this.renderCustomGradesCell(kpi)
|
|
749
|
+
default:
|
|
750
|
+
if (!scoreType) {
|
|
751
|
+
return html`<span style="color:#ccc">미설정</span>`
|
|
752
|
+
}
|
|
753
|
+
hasGrades = false
|
|
754
|
+
label = '등급 설정 없음'
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
return html`<span
|
|
758
|
+
style="color:${hasGrades ? '#4caf50' : '#999'};cursor:pointer;"
|
|
759
|
+
@click=${() => this._editGrades(kpi)}
|
|
760
|
+
>${label}</span
|
|
761
|
+
>`
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
/**
|
|
765
|
+
* CUSTOM scoreType의 grades 셀 렌더링. 서브클래스에서 override하여 구현.
|
|
766
|
+
*/
|
|
767
|
+
protected renderCustomGradesCell(kpi: any): any {
|
|
768
|
+
return html`<span style="color:#999;cursor:pointer;" @click=${() => this._editGrades(kpi)}>커스텀 설정됨</span>`
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
/**
|
|
772
|
+
* 등급 편집 팝업. CUSTOM 타입은 _editCustomGrades()로 위임. 서브클래스에서 override 가능.
|
|
773
|
+
*/
|
|
774
|
+
protected async _editGrades(kpi: any) {
|
|
775
|
+
if (!kpi.id) {
|
|
776
|
+
notify({ message: 'KPI를 먼저 저장한 후에 등급 설정을 할 수 있습니다.' })
|
|
777
|
+
return
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
const scoreType = inferScoreType(kpi)
|
|
659
781
|
|
|
782
|
+
if (scoreType === KPI_SCORE_TYPE.CUSTOM) {
|
|
783
|
+
await this._editCustomGrades(kpi)
|
|
660
784
|
return
|
|
661
|
-
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
if (scoreType !== KPI_SCORE_TYPE.LOOKUP) return
|
|
662
788
|
|
|
663
|
-
const popup = await openPopup(html` <kpi-grade-editor .kpi=${kpi}></kpi-grade-editor> `, {
|
|
789
|
+
const popup = await openPopup(html` <kpi-grade-editor .kpi=${kpi}></kpi-grade-editor> `, {
|
|
790
|
+
title: `${kpi.name} - 등급 설정`,
|
|
664
791
|
size: 'large'
|
|
665
|
-
|
|
792
|
+
})
|
|
793
|
+
popup.onclosed = () => { this.grist.fetch() }
|
|
794
|
+
}
|
|
666
795
|
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
796
|
+
/**
|
|
797
|
+
* CUSTOM scoreType의 등급 편집 팝업. 서브클래스에서 override하여 구현.
|
|
798
|
+
*/
|
|
799
|
+
protected async _editCustomGrades(kpi: any) {
|
|
800
|
+
notify({ message: '커스텀 등급 편집기가 필요합니다.' })
|
|
801
|
+
}
|
|
670
802
|
|
|
671
803
|
async _editViz(kpi: any) { const popup = await openPopup(
|
|
672
804
|
html`
|
|
@@ -733,6 +865,7 @@ export class KpiListPage extends p13n(localize(i18next)(ScopedElementsMixin(Page
|
|
|
733
865
|
active
|
|
734
866
|
formula
|
|
735
867
|
periodType
|
|
868
|
+
scoreType
|
|
736
869
|
scoreFormula
|
|
737
870
|
grades
|
|
738
871
|
vizType
|
|
@@ -329,7 +329,7 @@ export class KpiMapPanel extends LitElement {
|
|
|
329
329
|
this.onPeriodChange()
|
|
330
330
|
}}
|
|
331
331
|
>
|
|
332
|
-
<option value="
|
|
332
|
+
<option value="2026">2026년</option>
|
|
333
333
|
<option value="2025">2025년</option>
|
|
334
334
|
<option value="2024">2024년</option>
|
|
335
335
|
<option value="2023">2023년</option>
|
|
@@ -366,7 +366,7 @@ export class KpiMapPanel extends LitElement {
|
|
|
366
366
|
this.onPeriodChange()
|
|
367
367
|
}}
|
|
368
368
|
>
|
|
369
|
-
<option value="
|
|
369
|
+
<option value="2026">2026년</option>
|
|
370
370
|
<option value="2025">2025년</option>
|
|
371
371
|
<option value="2024">2024년</option>
|
|
372
372
|
<option value="2023">2023년</option>
|
|
@@ -161,11 +161,25 @@ export class KpiMetricListPage extends p13n(localize(i18next)(ScopedElementsMixi
|
|
|
161
161
|
{ type: 'string', name: 'description', header: '설명', record: { editable: true }, width: 200 },
|
|
162
162
|
{ type: 'string', name: 'unit', header: '단위', record: { editable: true }, width: 80 },
|
|
163
163
|
{ type: 'string', name: 'source', header: '데이터출처', record: { editable: true }, width: 120 },
|
|
164
|
-
{ type: '
|
|
164
|
+
{ type: 'resource-object',
|
|
165
165
|
name: 'dataSet',
|
|
166
166
|
header: '데이터셋',
|
|
167
|
-
record: {
|
|
168
|
-
|
|
167
|
+
record: {
|
|
168
|
+
editable: true,
|
|
169
|
+
options: {
|
|
170
|
+
title: '데이터셋 선택',
|
|
171
|
+
queryName: 'dataSets',
|
|
172
|
+
columns: [
|
|
173
|
+
{ name: 'id', hidden: true },
|
|
174
|
+
{ name: 'name', header: '이름', filter: 'search' },
|
|
175
|
+
{ name: 'description', header: '설명' }
|
|
176
|
+
],
|
|
177
|
+
list: { fields: ['name', 'description'] },
|
|
178
|
+
nameField: 'name',
|
|
179
|
+
descriptionField: ''
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
width: 150
|
|
169
183
|
},
|
|
170
184
|
{ type: 'string', name: 'fieldName', header: '필드명', record: { editable: true }, width: 120 },
|
|
171
185
|
{ type: 'select',
|
package/dist-client/index.d.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { KpiListPage } from './pages/kpi/kpi-list-page.js';
|
package/dist-client/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
export { KpiListPage } from './pages/kpi/kpi-list-page.js';
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist-client/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../client/index.ts"],"names":[],"mappings":"","sourcesContent":[""]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAA","sourcesContent":["export { KpiListPage } from './pages/kpi/kpi-list-page.js'\n"]}
|
|
@@ -6,7 +6,36 @@ import '@operato/data-grist/ox-record-creator.js';
|
|
|
6
6
|
import './kpi-viz-editor.js';
|
|
7
7
|
import './kpi-grade-editor.js';
|
|
8
8
|
import { PageView } from '@operato/shell';
|
|
9
|
-
import { FetchOption } from '@operato/data-grist';
|
|
9
|
+
import { DataGrist, FetchOption } from '@operato/data-grist';
|
|
10
|
+
/**
|
|
11
|
+
* KPI scoreType — value → score 변환 방식 (null이면 미설정)
|
|
12
|
+
*
|
|
13
|
+
* DIRECT — value = score, 변환 없음
|
|
14
|
+
* FORMULA — scoreFormula 수식으로 변환
|
|
15
|
+
* LOOKUP — grade table(1D)로 변환
|
|
16
|
+
* CUSTOM — 2D 룩업 등 특수 변환
|
|
17
|
+
*/
|
|
18
|
+
export declare const KPI_SCORE_TYPE: {
|
|
19
|
+
readonly DIRECT: "DIRECT";
|
|
20
|
+
readonly FORMULA: "FORMULA";
|
|
21
|
+
readonly LOOKUP: "LOOKUP";
|
|
22
|
+
readonly CUSTOM: "CUSTOM";
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* KPI valueType — value 획득 방식
|
|
26
|
+
*
|
|
27
|
+
* MEASURED — 외부 시스템 수집 측정값
|
|
28
|
+
* ASSESSED — 감리자 직접 평가 (1~5)
|
|
29
|
+
* CALCULATED — formula 자동 계산
|
|
30
|
+
* COMPOSITE — 다차원 복합 입력
|
|
31
|
+
*/
|
|
32
|
+
export declare const KPI_VALUE_TYPE: {
|
|
33
|
+
readonly MEASURED: "MEASURED";
|
|
34
|
+
readonly ASSESSED: "ASSESSED";
|
|
35
|
+
readonly CALCULATED: "CALCULATED";
|
|
36
|
+
readonly COMPOSITE: "COMPOSITE";
|
|
37
|
+
};
|
|
38
|
+
export declare function inferScoreType(kpi: any): string;
|
|
10
39
|
import { KpiImporter } from './kpi-importer';
|
|
11
40
|
import { KpiGradeEditor } from './kpi-grade-editor';
|
|
12
41
|
import { KpiVizEditor } from './kpi-viz-editor';
|
|
@@ -25,7 +54,7 @@ export declare class KpiListPage extends KpiListPage_base {
|
|
|
25
54
|
};
|
|
26
55
|
gristConfig: any;
|
|
27
56
|
mode: 'CARD' | 'GRID' | 'LIST';
|
|
28
|
-
|
|
57
|
+
protected grist: DataGrist;
|
|
29
58
|
availableVariables: any[];
|
|
30
59
|
availableVariablesLoaded: boolean;
|
|
31
60
|
hierarchicalView: boolean;
|
|
@@ -77,7 +106,23 @@ export declare class KpiListPage extends KpiListPage_base {
|
|
|
77
106
|
creationCallback(kpi: any): Promise<boolean>;
|
|
78
107
|
exportHandler(): Promise<{}[]>;
|
|
79
108
|
importHandler(records: any): Promise<void>;
|
|
80
|
-
|
|
109
|
+
/**
|
|
110
|
+
* grades 셀 렌더러. 서브클래스에서 override 가능.
|
|
111
|
+
* CUSTOM scoreType은 renderCustomGradesCell()로 위임.
|
|
112
|
+
*/
|
|
113
|
+
protected renderGradesCell(kpi: any): any;
|
|
114
|
+
/**
|
|
115
|
+
* CUSTOM scoreType의 grades 셀 렌더링. 서브클래스에서 override하여 구현.
|
|
116
|
+
*/
|
|
117
|
+
protected renderCustomGradesCell(kpi: any): any;
|
|
118
|
+
/**
|
|
119
|
+
* 등급 편집 팝업. CUSTOM 타입은 _editCustomGrades()로 위임. 서브클래스에서 override 가능.
|
|
120
|
+
*/
|
|
121
|
+
protected _editGrades(kpi: any): Promise<void>;
|
|
122
|
+
/**
|
|
123
|
+
* CUSTOM scoreType의 등급 편집 팝업. 서브클래스에서 override하여 구현.
|
|
124
|
+
*/
|
|
125
|
+
protected _editCustomGrades(kpi: any): Promise<void>;
|
|
81
126
|
_editViz(kpi: any): Promise<void>;
|
|
82
127
|
_onVizUpdated(kpiId: string, vizType: string, vizMeta: any): Promise<void>;
|
|
83
128
|
_calculateKpiValue(kpi: any): Promise<void>;
|
|
@@ -19,6 +19,45 @@ import { OxPrompt } from '@operato/popup';
|
|
|
19
19
|
import { isMobileDevice } from '@operato/utils';
|
|
20
20
|
import { p13n } from '@operato/p13n';
|
|
21
21
|
import gql from 'graphql-tag';
|
|
22
|
+
/**
|
|
23
|
+
* KPI scoreType — value → score 변환 방식 (null이면 미설정)
|
|
24
|
+
*
|
|
25
|
+
* DIRECT — value = score, 변환 없음
|
|
26
|
+
* FORMULA — scoreFormula 수식으로 변환
|
|
27
|
+
* LOOKUP — grade table(1D)로 변환
|
|
28
|
+
* CUSTOM — 2D 룩업 등 특수 변환
|
|
29
|
+
*/
|
|
30
|
+
export const KPI_SCORE_TYPE = {
|
|
31
|
+
DIRECT: 'DIRECT',
|
|
32
|
+
FORMULA: 'FORMULA',
|
|
33
|
+
LOOKUP: 'LOOKUP',
|
|
34
|
+
CUSTOM: 'CUSTOM'
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* KPI valueType — value 획득 방식
|
|
38
|
+
*
|
|
39
|
+
* MEASURED — 외부 시스템 수집 측정값
|
|
40
|
+
* ASSESSED — 감리자 직접 평가 (1~5)
|
|
41
|
+
* CALCULATED — formula 자동 계산
|
|
42
|
+
* COMPOSITE — 다차원 복합 입력
|
|
43
|
+
*/
|
|
44
|
+
export const KPI_VALUE_TYPE = {
|
|
45
|
+
MEASURED: 'MEASURED',
|
|
46
|
+
ASSESSED: 'ASSESSED',
|
|
47
|
+
CALCULATED: 'CALCULATED',
|
|
48
|
+
COMPOSITE: 'COMPOSITE'
|
|
49
|
+
};
|
|
50
|
+
export function inferScoreType(kpi) {
|
|
51
|
+
if (kpi.scoreType)
|
|
52
|
+
return kpi.scoreType;
|
|
53
|
+
if (kpi.scoreFormula)
|
|
54
|
+
return KPI_SCORE_TYPE.FORMULA;
|
|
55
|
+
if (kpi.grades && typeof kpi.grades === 'object' && !Array.isArray(kpi.grades))
|
|
56
|
+
return KPI_SCORE_TYPE.CUSTOM;
|
|
57
|
+
if (Array.isArray(kpi.grades) && kpi.grades.length > 0)
|
|
58
|
+
return KPI_SCORE_TYPE.LOOKUP;
|
|
59
|
+
return KPI_SCORE_TYPE.DIRECT;
|
|
60
|
+
}
|
|
22
61
|
import { KpiImporter } from './kpi-importer';
|
|
23
62
|
import { KpiGradeEditor } from './kpi-grade-editor';
|
|
24
63
|
import { KpiVizEditor } from './kpi-viz-editor';
|
|
@@ -383,21 +422,42 @@ let KpiListPage = class KpiListPage extends p13n(localize(i18next)(ScopedElement
|
|
|
383
422
|
name: 'grades',
|
|
384
423
|
header: '성과지수 Lookup',
|
|
385
424
|
record: { editable: false,
|
|
386
|
-
renderer: (v, c, r) =>
|
|
387
|
-
if (r.grades && r.grades.length > 0) {
|
|
388
|
-
return html `<span style="color: #4caf50; cursor: pointer;" @click=${() => this._editGrades(r)}>
|
|
389
|
-
${r.grades.length}개 등급 설정됨
|
|
390
|
-
</span>`;
|
|
391
|
-
}
|
|
392
|
-
else {
|
|
393
|
-
return html `<span style="color: #999; cursor: pointer;" @click=${() => this._editGrades(r)}>
|
|
394
|
-
등급 설정 없음
|
|
395
|
-
</span>`;
|
|
396
|
-
}
|
|
397
|
-
}
|
|
425
|
+
renderer: (v, c, r) => this.renderGradesCell(r)
|
|
398
426
|
},
|
|
399
427
|
width: 150
|
|
400
428
|
},
|
|
429
|
+
{
|
|
430
|
+
type: 'select',
|
|
431
|
+
name: 'scoreType',
|
|
432
|
+
header: 'Score 산정',
|
|
433
|
+
record: {
|
|
434
|
+
editable: true,
|
|
435
|
+
options: [
|
|
436
|
+
{ value: '', display: '(미설정)' },
|
|
437
|
+
{ value: 'DIRECT', display: 'DIRECT (변환없음)' },
|
|
438
|
+
{ value: 'FORMULA', display: 'FORMULA (수식)' },
|
|
439
|
+
{ value: 'LOOKUP', display: 'LOOKUP (등급표)' },
|
|
440
|
+
{ value: 'CUSTOM', display: 'CUSTOM (특수)' }
|
|
441
|
+
]
|
|
442
|
+
},
|
|
443
|
+
width: 130
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
type: 'select',
|
|
447
|
+
name: 'valueType',
|
|
448
|
+
header: 'Value 획득',
|
|
449
|
+
record: {
|
|
450
|
+
editable: true,
|
|
451
|
+
options: [
|
|
452
|
+
{ value: '', display: '(미설정)' },
|
|
453
|
+
{ value: 'MEASURED', display: 'MEASURED (외부수집)' },
|
|
454
|
+
{ value: 'ASSESSED', display: 'ASSESSED (감리자평가)' },
|
|
455
|
+
{ value: 'CALCULATED', display: 'CALCULATED (산식)' },
|
|
456
|
+
{ value: 'COMPOSITE', display: 'COMPOSITE (복합)' }
|
|
457
|
+
]
|
|
458
|
+
},
|
|
459
|
+
width: 140
|
|
460
|
+
},
|
|
401
461
|
{ type: 'number',
|
|
402
462
|
name: 'weight',
|
|
403
463
|
header: '가중치',
|
|
@@ -486,6 +546,8 @@ let KpiListPage = class KpiListPage extends p13n(localize(i18next)(ScopedElement
|
|
|
486
546
|
active
|
|
487
547
|
formula
|
|
488
548
|
periodType
|
|
549
|
+
scoreType
|
|
550
|
+
valueType
|
|
489
551
|
scoreFormula
|
|
490
552
|
grades
|
|
491
553
|
vizType
|
|
@@ -648,18 +710,77 @@ let KpiListPage = class KpiListPage extends p13n(localize(i18next)(ScopedElement
|
|
|
648
710
|
this.grist.fetch();
|
|
649
711
|
};
|
|
650
712
|
}
|
|
713
|
+
/**
|
|
714
|
+
* grades 셀 렌더러. 서브클래스에서 override 가능.
|
|
715
|
+
* CUSTOM scoreType은 renderCustomGradesCell()로 위임.
|
|
716
|
+
*/
|
|
717
|
+
renderGradesCell(kpi) {
|
|
718
|
+
const scoreType = inferScoreType(kpi);
|
|
719
|
+
let label;
|
|
720
|
+
let hasGrades;
|
|
721
|
+
switch (scoreType) {
|
|
722
|
+
case KPI_SCORE_TYPE.LOOKUP:
|
|
723
|
+
hasGrades = Array.isArray(kpi.grades) && kpi.grades.length > 0;
|
|
724
|
+
label = hasGrades ? `${kpi.grades.length}개 등급 설정됨` : '등급 설정 없음';
|
|
725
|
+
break;
|
|
726
|
+
case KPI_SCORE_TYPE.DIRECT:
|
|
727
|
+
// valueType으로 세분화 표시
|
|
728
|
+
if (kpi.valueType === KPI_VALUE_TYPE.ASSESSED) {
|
|
729
|
+
return html `<span style="color:#2196f3">평가형 (1~5)</span>`;
|
|
730
|
+
}
|
|
731
|
+
if (kpi.valueType === KPI_VALUE_TYPE.CALCULATED) {
|
|
732
|
+
return html `<span style="color:#888">산식 계산</span>`;
|
|
733
|
+
}
|
|
734
|
+
return html `<span style="color:#888">직접 (value=score)</span>`;
|
|
735
|
+
case KPI_SCORE_TYPE.FORMULA:
|
|
736
|
+
return html `<span style="color:#888">산식 변환</span>`;
|
|
737
|
+
case KPI_SCORE_TYPE.CUSTOM:
|
|
738
|
+
return this.renderCustomGradesCell(kpi);
|
|
739
|
+
default:
|
|
740
|
+
if (!scoreType) {
|
|
741
|
+
return html `<span style="color:#ccc">미설정</span>`;
|
|
742
|
+
}
|
|
743
|
+
hasGrades = false;
|
|
744
|
+
label = '등급 설정 없음';
|
|
745
|
+
}
|
|
746
|
+
return html `<span
|
|
747
|
+
style="color:${hasGrades ? '#4caf50' : '#999'};cursor:pointer;"
|
|
748
|
+
@click=${() => this._editGrades(kpi)}
|
|
749
|
+
>${label}</span
|
|
750
|
+
>`;
|
|
751
|
+
}
|
|
752
|
+
/**
|
|
753
|
+
* CUSTOM scoreType의 grades 셀 렌더링. 서브클래스에서 override하여 구현.
|
|
754
|
+
*/
|
|
755
|
+
renderCustomGradesCell(kpi) {
|
|
756
|
+
return html `<span style="color:#999;cursor:pointer;" @click=${() => this._editGrades(kpi)}>커스텀 설정됨</span>`;
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* 등급 편집 팝업. CUSTOM 타입은 _editCustomGrades()로 위임. 서브클래스에서 override 가능.
|
|
760
|
+
*/
|
|
651
761
|
async _editGrades(kpi) {
|
|
652
762
|
if (!kpi.id) {
|
|
653
|
-
notify({ message: 'KPI를 먼저 저장한 후에 등급 설정을 할 수 있습니다.'
|
|
654
|
-
});
|
|
763
|
+
notify({ message: 'KPI를 먼저 저장한 후에 등급 설정을 할 수 있습니다.' });
|
|
655
764
|
return;
|
|
656
765
|
}
|
|
657
|
-
const
|
|
766
|
+
const scoreType = inferScoreType(kpi);
|
|
767
|
+
if (scoreType === KPI_SCORE_TYPE.CUSTOM) {
|
|
768
|
+
await this._editCustomGrades(kpi);
|
|
769
|
+
return;
|
|
770
|
+
}
|
|
771
|
+
if (scoreType !== KPI_SCORE_TYPE.LOOKUP)
|
|
772
|
+
return;
|
|
773
|
+
const popup = await openPopup(html ` <kpi-grade-editor .kpi=${kpi}></kpi-grade-editor> `, {
|
|
774
|
+
title: `${kpi.name} - 등급 설정`,
|
|
658
775
|
size: 'large'
|
|
659
776
|
});
|
|
660
|
-
popup.onclosed = () => {
|
|
661
|
-
|
|
662
|
-
|
|
777
|
+
popup.onclosed = () => { this.grist.fetch(); };
|
|
778
|
+
}
|
|
779
|
+
/**
|
|
780
|
+
* CUSTOM scoreType의 등급 편집 팝업. 서브클래스에서 override하여 구현.
|
|
781
|
+
*/
|
|
782
|
+
async _editCustomGrades(kpi) {
|
|
783
|
+
notify({ message: '커스텀 등급 편집기가 필요합니다.' });
|
|
663
784
|
}
|
|
664
785
|
async _editViz(kpi) {
|
|
665
786
|
const popup = await openPopup(html `
|
|
@@ -731,6 +852,7 @@ let KpiListPage = class KpiListPage extends p13n(localize(i18next)(ScopedElement
|
|
|
731
852
|
active
|
|
732
853
|
formula
|
|
733
854
|
periodType
|
|
855
|
+
scoreType
|
|
734
856
|
scoreFormula
|
|
735
857
|
grades
|
|
736
858
|
vizType
|