@things-factory/kpi 9.0.17 → 9.0.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. package/client/bootstrap.ts +8 -0
  2. package/client/pages/kpi/kpi-list-page.ts +99 -11
  3. package/client/pages/kpi/kpi-viz-editor.ts +214 -14
  4. package/client/pages/kpi-category/kpi-category-list-page.ts +80 -8
  5. package/client/pages/kpi-history/kpi-history-list-page.ts +1 -1
  6. package/client/pages/kpi-metric/kpi-metric-list-page.ts +31 -7
  7. package/client/pages/kpi-metric-value/kpi-metric-value-importer.ts +65 -0
  8. package/client/pages/kpi-metric-value/kpi-metric-value-list-page.ts +299 -0
  9. package/client/pages/{kpi-value/kpi-value-manual-entry-form.ts → kpi-metric-value/kpi-metric-value-manual-entry-form.ts} +18 -44
  10. package/client/pages/{kpi-value/kpi-value-manual-entry-page.ts → kpi-metric-value/kpi-metric-value-manual-entry-page.ts} +21 -21
  11. package/client/pages/kpi-value/kpi-value-list-page.ts +4 -6
  12. package/client/route.ts +6 -2
  13. package/dist-client/bootstrap.d.ts +2 -0
  14. package/dist-client/bootstrap.js +7 -0
  15. package/dist-client/bootstrap.js.map +1 -0
  16. package/dist-client/pages/kpi/kpi-list-page.d.ts +6 -0
  17. package/dist-client/pages/kpi/kpi-list-page.js +100 -11
  18. package/dist-client/pages/kpi/kpi-list-page.js.map +1 -1
  19. package/dist-client/pages/kpi/kpi-viz-editor.js +208 -14
  20. package/dist-client/pages/kpi/kpi-viz-editor.js.map +1 -1
  21. package/dist-client/pages/kpi-category/kpi-category-list-page.d.ts +5 -0
  22. package/dist-client/pages/kpi-category/kpi-category-list-page.js +83 -8
  23. package/dist-client/pages/kpi-category/kpi-category-list-page.js.map +1 -1
  24. package/dist-client/pages/kpi-history/kpi-history-list-page.js +1 -1
  25. package/dist-client/pages/kpi-history/kpi-history-list-page.js.map +1 -1
  26. package/dist-client/pages/kpi-metric/kpi-metric-list-page.js +29 -5
  27. package/dist-client/pages/kpi-metric/kpi-metric-list-page.js.map +1 -1
  28. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.d.ts +23 -0
  29. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js +75 -0
  30. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js.map +1 -0
  31. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.d.ts +61 -0
  32. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js +301 -0
  33. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js.map +1 -0
  34. package/dist-client/pages/{kpi-value/kpi-value-manual-entry-form.d.ts → kpi-metric-value/kpi-metric-value-manual-entry-form.d.ts} +3 -5
  35. package/dist-client/pages/{kpi-value/kpi-value-manual-entry-form.js → kpi-metric-value/kpi-metric-value-manual-entry-form.js} +27 -56
  36. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js.map +1 -0
  37. package/dist-client/pages/{kpi-value/kpi-value-manual-entry-page.d.ts → kpi-metric-value/kpi-metric-value-manual-entry-page.d.ts} +5 -5
  38. package/dist-client/pages/{kpi-value/kpi-value-manual-entry-page.js → kpi-metric-value/kpi-metric-value-manual-entry-page.js} +28 -28
  39. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js.map +1 -0
  40. package/dist-client/pages/kpi-value/kpi-value-list-page.js +4 -6
  41. package/dist-client/pages/kpi-value/kpi-value-list-page.js.map +1 -1
  42. package/dist-client/route.d.ts +1 -1
  43. package/dist-client/route.js +5 -2
  44. package/dist-client/route.js.map +1 -1
  45. package/dist-client/tsconfig.tsbuildinfo +1 -1
  46. package/dist-server/service/index.d.ts +4 -2
  47. package/dist-server/service/index.js +6 -1
  48. package/dist-server/service/index.js.map +1 -1
  49. package/dist-server/service/kpi/aggregate-kpi.js +5 -7
  50. package/dist-server/service/kpi/aggregate-kpi.js.map +1 -1
  51. package/dist-server/service/kpi/kpi-history.d.ts +3 -1
  52. package/dist-server/service/kpi/kpi-history.js +10 -0
  53. package/dist-server/service/kpi/kpi-history.js.map +1 -1
  54. package/dist-server/service/kpi/kpi-mutation.js +1 -1
  55. package/dist-server/service/kpi/kpi-mutation.js.map +1 -1
  56. package/dist-server/service/kpi/kpi-type.d.ts +2 -0
  57. package/dist-server/service/kpi/kpi-type.js +8 -0
  58. package/dist-server/service/kpi/kpi-type.js.map +1 -1
  59. package/dist-server/service/kpi/kpi.d.ts +9 -0
  60. package/dist-server/service/kpi/kpi.js +23 -1
  61. package/dist-server/service/kpi/kpi.js.map +1 -1
  62. package/dist-server/service/kpi-category/kpi-category-mutation.js +0 -8
  63. package/dist-server/service/kpi-category/kpi-category-mutation.js.map +1 -1
  64. package/dist-server/service/kpi-category/kpi-category-type.d.ts +4 -2
  65. package/dist-server/service/kpi-category/kpi-category-type.js +16 -8
  66. package/dist-server/service/kpi-category/kpi-category-type.js.map +1 -1
  67. package/dist-server/service/kpi-category/kpi-category.d.ts +2 -2
  68. package/dist-server/service/kpi-category/kpi-category.js +8 -8
  69. package/dist-server/service/kpi-category/kpi-category.js.map +1 -1
  70. package/dist-server/service/kpi-metric/aggregate-kpi-metric.js +31 -74
  71. package/dist-server/service/kpi-metric/aggregate-kpi-metric.js.map +1 -1
  72. package/dist-server/service/kpi-metric/kpi-metric-mutation.d.ts +1 -1
  73. package/dist-server/service/kpi-metric/kpi-metric-mutation.js +15 -28
  74. package/dist-server/service/kpi-metric/kpi-metric-mutation.js.map +1 -1
  75. package/dist-server/service/kpi-metric/kpi-metric-type.d.ts +6 -4
  76. package/dist-server/service/kpi-metric/kpi-metric-type.js +20 -12
  77. package/dist-server/service/kpi-metric/kpi-metric-type.js.map +1 -1
  78. package/dist-server/service/kpi-metric/kpi-metric.d.ts +15 -2
  79. package/dist-server/service/kpi-metric/kpi-metric.js +34 -14
  80. package/dist-server/service/kpi-metric/kpi-metric.js.map +1 -1
  81. package/dist-server/service/kpi-metric-value/index.d.ts +6 -0
  82. package/dist-server/service/kpi-metric-value/index.js +10 -0
  83. package/dist-server/service/kpi-metric-value/index.js.map +1 -0
  84. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.d.ts +11 -0
  85. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js +229 -0
  86. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js.map +1 -0
  87. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.d.ts +13 -0
  88. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js +95 -0
  89. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js.map +1 -0
  90. package/dist-server/service/kpi-metric-value/kpi-metric-value-type.d.ts +26 -0
  91. package/dist-server/service/kpi-metric-value/kpi-metric-value-type.js +112 -0
  92. package/dist-server/service/kpi-metric-value/kpi-metric-value-type.js.map +1 -0
  93. package/dist-server/service/kpi-metric-value/kpi-metric-value.d.ts +23 -0
  94. package/dist-server/service/kpi-metric-value/kpi-metric-value.js +106 -0
  95. package/dist-server/service/kpi-metric-value/kpi-metric-value.js.map +1 -0
  96. package/dist-server/service/kpi-value/kpi-value-mutation.js +1 -2
  97. package/dist-server/service/kpi-value/kpi-value-mutation.js.map +1 -1
  98. package/dist-server/service/kpi-value/kpi-value-query.js +1 -1
  99. package/dist-server/service/kpi-value/kpi-value-query.js.map +1 -1
  100. package/dist-server/service/kpi-value/kpi-value-type.d.ts +2 -4
  101. package/dist-server/service/kpi-value/kpi-value-type.js +4 -18
  102. package/dist-server/service/kpi-value/kpi-value-type.js.map +1 -1
  103. package/dist-server/service/kpi-value/kpi-value.d.ts +3 -3
  104. package/dist-server/service/kpi-value/kpi-value.js +13 -14
  105. package/dist-server/service/kpi-value/kpi-value.js.map +1 -1
  106. package/dist-server/tsconfig.tsbuildinfo +1 -1
  107. package/package.json +3 -3
  108. package/server/service/index.ts +6 -1
  109. package/server/service/kpi/aggregate-kpi.ts +5 -8
  110. package/server/service/kpi/kpi-history.ts +9 -1
  111. package/server/service/kpi/kpi-mutation.ts +1 -1
  112. package/server/service/kpi/kpi-type.ts +6 -0
  113. package/server/service/kpi/kpi.ts +21 -0
  114. package/server/service/kpi-category/kpi-category-mutation.ts +0 -10
  115. package/server/service/kpi-category/kpi-category-type.ts +12 -6
  116. package/server/service/kpi-category/kpi-category.ts +6 -6
  117. package/server/service/kpi-metric/aggregate-kpi-metric.ts +29 -69
  118. package/server/service/kpi-metric/kpi-metric-mutation.ts +15 -26
  119. package/server/service/kpi-metric/kpi-metric-type.ts +17 -12
  120. package/server/service/kpi-metric/kpi-metric.ts +32 -11
  121. package/server/service/kpi-metric-value/index.ts +7 -0
  122. package/server/service/kpi-metric-value/kpi-metric-value-mutation.ts +215 -0
  123. package/server/service/kpi-metric-value/kpi-metric-value-query.ts +60 -0
  124. package/server/service/kpi-metric-value/kpi-metric-value-type.ts +82 -0
  125. package/server/service/kpi-metric-value/kpi-metric-value.ts +91 -0
  126. package/server/service/kpi-value/kpi-value-mutation.ts +1 -2
  127. package/server/service/kpi-value/kpi-value-query.ts +1 -1
  128. package/server/service/kpi-value/kpi-value-type.ts +4 -16
  129. package/server/service/kpi-value/kpi-value.ts +14 -14
  130. package/things-factory.config.js +5 -3
  131. package/translations/en.json +8 -1
  132. package/translations/ja.json +8 -1
  133. package/translations/ko.json +9 -2
  134. package/translations/ms.json +9 -2
  135. package/translations/zh.json +8 -1
  136. package/dist-client/pages/kpi-value/kpi-value-manual-entry-form.js.map +0 -1
  137. package/dist-client/pages/kpi-value/kpi-value-manual-entry-page.js.map +0 -1
@@ -0,0 +1,65 @@
1
+ import '@material/web/icon/icon.js'
2
+ import '@operato/data-grist'
3
+ import gql from 'graphql-tag'
4
+ import { css, html, LitElement } from 'lit'
5
+ import { property } from 'lit/decorators.js'
6
+ import { client } from '@operato/graphql'
7
+ import { i18next } from '@operato/i18n'
8
+ import { isMobileDevice } from '@operato/utils'
9
+ import { ButtonContainerStyles } from '@operato/styles'
10
+
11
+ export class KpiMetricValueImporter extends LitElement {
12
+ static styles = [
13
+ ButtonContainerStyles,
14
+ css`
15
+ :host {
16
+ display: flex;
17
+ flex-direction: column;
18
+ background-color: #fff;
19
+ }
20
+ ox-grist {
21
+ flex: 1;
22
+ }
23
+ `
24
+ ]
25
+
26
+ @property({ type: Array }) metricValues: any[] = []
27
+ @property({ type: Object }) columns = {
28
+ list: { fields: ['metric', 'value', 'valueDate', 'group', 'meta'] },
29
+ pagination: { infinite: true },
30
+ columns: [
31
+ { type: 'string', name: 'metric', header: 'Metric', width: 150 },
32
+ { type: 'number', name: 'value', header: '값', width: 120 },
33
+ { type: 'date', name: 'valueDate', header: '날짜', width: 120 },
34
+ { type: 'string', name: 'group', header: '그룹', width: 120 },
35
+ { type: 'object', name: 'meta', header: '메타', width: 120 }
36
+ ]
37
+ }
38
+
39
+ render() {
40
+ return html`
41
+ <ox-grist
42
+ .mode=${isMobileDevice() ? 'LIST' : 'GRID'}
43
+ .config=${this.columns}
44
+ .data=${{ records: this.metricValues }}
45
+ ></ox-grist>
46
+ <div class="button-container">
47
+ <button @click="${this.save.bind(this)}"><md-icon>save</md-icon>${i18next.t('button.save')}</button>
48
+ </div>
49
+ `
50
+ }
51
+
52
+ async save() {
53
+ const response = await client.mutate({
54
+ mutation: gql`
55
+ mutation importKpiMetricValues($metricValues: [KpiMetricValuePatch!]!) {
56
+ importKpiMetricValues(metricValues: $metricValues)
57
+ }
58
+ `,
59
+ variables: { metricValues: this.metricValues }
60
+ })
61
+ if (response.errors?.length) return
62
+ this.dispatchEvent(new CustomEvent('imported'))
63
+ }
64
+ }
65
+ customElements.define('kpi-metric-value-importer', KpiMetricValueImporter)
@@ -0,0 +1,299 @@
1
+ import '@material/web/icon/icon.js'
2
+ import '@material/web/button/elevated-button.js'
3
+ import '@operato/data-grist/ox-grist.js'
4
+ import '@operato/data-grist/ox-filters-form.js'
5
+ import '@operato/data-grist/ox-record-creator.js'
6
+
7
+ import { CommonButtonStyles, CommonHeaderStyles, CommonGristStyles, ScrollbarStyles } from '@operato/styles'
8
+ import { PageView, store } from '@operato/shell'
9
+ import { css, html } from 'lit'
10
+ import { customElement, property, query } from 'lit/decorators.js'
11
+ import { ScopedElementsMixin } from '@open-wc/scoped-elements'
12
+ import { ColumnConfig, DataGrist, FetchOption } from '@operato/data-grist'
13
+ import { client } from '@operato/graphql'
14
+ import { i18next, localize } from '@operato/i18n'
15
+ import { notify, openPopup } from '@operato/layout'
16
+ import { OxPopup, OxPrompt } from '@operato/popup'
17
+ import { isMobileDevice } from '@operato/utils'
18
+
19
+ import { connect } from 'pwa-helpers/connect-mixin'
20
+ import gql from 'graphql-tag'
21
+
22
+ @customElement('kpi-metric-value-list-page')
23
+ export class KpiMetricValueListPage extends connect(store)(localize(i18next)(ScopedElementsMixin(PageView))) {
24
+ static styles = [
25
+ ScrollbarStyles,
26
+ CommonGristStyles,
27
+ CommonHeaderStyles,
28
+ css`
29
+ :host {
30
+ display: flex;
31
+ width: 100%;
32
+ }
33
+ ox-grist {
34
+ overflow-y: auto;
35
+ flex: 1;
36
+ }
37
+ ox-filters-form {
38
+ flex: 1;
39
+ }
40
+ `
41
+ ]
42
+
43
+ @property({ type: Object }) gristConfig: any
44
+ @property({ type: String }) mode: 'CARD' | 'GRID' | 'LIST' = isMobileDevice() ? 'CARD' : 'GRID'
45
+ @query('ox-grist') private grist!: DataGrist
46
+
47
+ get context() {
48
+ return {
49
+ title: i18next.t('title.kpi metric value list'),
50
+ search: {
51
+ handler: (search: string) => {
52
+ this.grist.searchText = search
53
+ },
54
+ value: this.grist.searchText
55
+ },
56
+ filter: {
57
+ handler: () => {
58
+ this.grist.toggleHeadroom()
59
+ }
60
+ },
61
+ help: 'kpi/kpi-metric-value',
62
+ actions: [
63
+ {
64
+ title: i18next.t('button.save'),
65
+ action: this._updateKpiMetricValue.bind(this),
66
+ ...CommonButtonStyles.save
67
+ },
68
+ {
69
+ title: i18next.t('button.delete'),
70
+ action: this._deleteKpiMetricValue.bind(this),
71
+ ...CommonButtonStyles.delete
72
+ }
73
+ ],
74
+ exportable: {
75
+ name: i18next.t('title.kpi metric value list'),
76
+ data: this.exportHandler.bind(this)
77
+ },
78
+ importable: {
79
+ handler: this.importHandler.bind(this)
80
+ }
81
+ }
82
+ }
83
+
84
+ render() {
85
+ const mode = this.mode || (isMobileDevice() ? 'CARD' : 'GRID')
86
+ return html`
87
+ <ox-grist .mode=${mode} .config=${this.gristConfig} .fetchHandler=${this.fetchHandler.bind(this)}>
88
+ <div slot="headroom" class="header">
89
+ <div class="filters">
90
+ <ox-filters-form autofocus without-search></ox-filters-form>
91
+ <div id="modes">
92
+ <md-icon @click=${() => (this.mode = 'GRID')} ?active=${mode == 'GRID'}>grid_on</md-icon>
93
+ <md-icon @click=${() => (this.mode = 'LIST')} ?active=${mode == 'LIST'}>format_list_bulleted</md-icon>
94
+ <md-icon @click=${() => (this.mode = 'CARD')} ?active=${mode == 'CARD'}>apps</md-icon>
95
+ </div>
96
+ <ox-record-creator id="add" .callback=${this.creationCallback.bind(this)}>
97
+ <button>
98
+ <md-icon>add</md-icon>
99
+ </button>
100
+ </ox-record-creator>
101
+ </div>
102
+ </div>
103
+ </ox-grist>
104
+ `
105
+ }
106
+
107
+ async pageInitialized() {
108
+ this.gristConfig = {
109
+ list: {
110
+ fields: ['metric', 'valueDate', 'value', 'group', 'meta', 'createdAt', 'updatedAt', 'creator', 'updater'],
111
+ details: ['metric', 'valueDate', 'value', 'group', 'meta', 'createdAt', 'updatedAt', 'creator', 'updater']
112
+ },
113
+ columns: [
114
+ { type: 'gutter', gutterName: 'sequence' },
115
+ { type: 'gutter', gutterName: 'row-selector', multiple: true },
116
+ {
117
+ type: 'string',
118
+ name: 'metric',
119
+ header: 'Metric',
120
+ record: { editable: false, renderer: (v, c, r) => r.metric?.name },
121
+ width: 150
122
+ },
123
+ { type: 'date', name: 'valueDate', header: '날짜', record: { editable: true }, width: 120 },
124
+ { type: 'number', name: 'value', header: '값', record: { editable: true }, width: 120 },
125
+ { type: 'string', name: 'group', header: '그룹', record: { editable: false }, width: 120 },
126
+ { type: 'object', name: 'meta', header: '메타', record: { editable: false }, width: 120 },
127
+ { type: 'datetime', name: 'createdAt', header: '생성일', record: { editable: false }, width: 180 },
128
+ { type: 'datetime', name: 'updatedAt', header: '수정일', record: { editable: false }, width: 180 },
129
+ {
130
+ type: 'resource-object',
131
+ name: 'creator',
132
+ header: '생성자',
133
+ record: { editable: false, renderer: (v, c, r) => r.creator?.name },
134
+ width: 120
135
+ },
136
+ {
137
+ type: 'resource-object',
138
+ name: 'updater',
139
+ header: '수정자',
140
+ record: { editable: false, renderer: (v, c, r) => r.updater?.name },
141
+ width: 120
142
+ }
143
+ ],
144
+ rows: {
145
+ appendable: false,
146
+ selectable: {
147
+ multiple: true
148
+ }
149
+ },
150
+ sorters: [{ name: 'valueDate' }]
151
+ }
152
+ }
153
+
154
+ async fetchHandler({ page = 1, limit = 100, sortings = [], filters = [] }: FetchOption) {
155
+ const response = await client.query({
156
+ query: gql`
157
+ query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {
158
+ responses: kpiMetricValues(filters: $filters, pagination: $pagination, sortings: $sortings) {
159
+ items {
160
+ id
161
+ metric {
162
+ id
163
+ name
164
+ }
165
+ valueDate
166
+ value
167
+ meta
168
+ group
169
+ updater {
170
+ id
171
+ name
172
+ }
173
+ updatedAt
174
+ creator {
175
+ id
176
+ name
177
+ }
178
+ createdAt
179
+ }
180
+ total
181
+ }
182
+ }
183
+ `,
184
+ variables: {
185
+ filters,
186
+ pagination: { page, limit },
187
+ sortings
188
+ }
189
+ })
190
+ return {
191
+ total: response.data.responses.total || 0,
192
+ records: response.data.responses.items || []
193
+ }
194
+ }
195
+
196
+ async _deleteKpiMetricValue() {
197
+ if (
198
+ await OxPrompt.open({
199
+ title: i18next.t('text.are_you_sure'),
200
+ text: i18next.t('text.sure_to_x', { x: i18next.t('text.delete') }),
201
+ confirmButton: { text: i18next.t('button.confirm') },
202
+ cancelButton: { text: i18next.t('button.cancel') }
203
+ })
204
+ ) {
205
+ const ids = this.grist.selected.map(record => record.id)
206
+ if (ids && ids.length > 0) {
207
+ const response = await client.mutate({
208
+ mutation: gql`
209
+ mutation ($ids: [String!]!) {
210
+ deleteKpiMetricValues(ids: $ids)
211
+ }
212
+ `,
213
+ variables: { ids }
214
+ })
215
+ if (!response.errors) {
216
+ this.grist.fetch()
217
+ notify({ message: i18next.t('text.info_x_successfully', { x: i18next.t('text.delete') }) })
218
+ }
219
+ }
220
+ }
221
+ }
222
+
223
+ async _updateKpiMetricValue() {
224
+ let patches = this.grist.dirtyRecords
225
+ if (patches && patches.length) {
226
+ patches = patches.map(patch => {
227
+ let patchField: any = patch.id ? { id: patch.id } : {}
228
+ const dirtyFields = patch.__dirtyfields__
229
+ for (let key in dirtyFields) {
230
+ patchField[key] = dirtyFields[key].after
231
+ }
232
+ patchField.cuFlag = patch.__dirty__
233
+ return patchField
234
+ })
235
+ const response = await client.mutate({
236
+ mutation: gql`
237
+ mutation ($patches: [KpiMetricValuePatch!]!) {
238
+ updateMultipleKpiMetricValue(patches: $patches) {
239
+ id
240
+ metric {
241
+ id
242
+ name
243
+ }
244
+ valueDate
245
+ value
246
+ }
247
+ }
248
+ `,
249
+ variables: { patches }
250
+ })
251
+ if (!response.errors) {
252
+ this.grist.fetch()
253
+ }
254
+ }
255
+ }
256
+
257
+ async creationCallback(metricValue) {
258
+ try {
259
+ const response = await client.mutate({
260
+ mutation: gql`
261
+ mutation ($metricValue: NewKpiMetricValue!) {
262
+ createKpiMetricValue(metricValue: $metricValue) {
263
+ id
264
+ }
265
+ }
266
+ `,
267
+ variables: { metricValue },
268
+ context: { hasUpload: true }
269
+ })
270
+ if (!response.errors) {
271
+ this.grist.fetch()
272
+ document.dispatchEvent(
273
+ new CustomEvent('notify', { detail: { message: i18next.t('text.data_created_successfully') } })
274
+ )
275
+ }
276
+ return true
277
+ } catch (ex) {
278
+ console.error(ex)
279
+ document.dispatchEvent(new CustomEvent('notify', { detail: { type: 'error', message: i18next.t('text.error') } }))
280
+ return false
281
+ }
282
+ }
283
+
284
+ async exportHandler() {
285
+ const exportTargets = this.grist.selected.length ? this.grist.selected : this.grist.dirtyData.records
286
+ const targetFieldSet = new Set(['id', 'metric', 'valueDate', 'value', 'group', 'meta'])
287
+ return exportTargets.map(metricValue => {
288
+ let tempObj = {}
289
+ for (const field of targetFieldSet) {
290
+ tempObj[field] = metricValue[field]
291
+ }
292
+ return tempObj
293
+ })
294
+ }
295
+
296
+ async importHandler(records) {
297
+ // 임포트 팝업 등은 추후 구현
298
+ }
299
+ }
@@ -1,5 +1,3 @@
1
- import '@material/web/icon/icon.js'
2
-
3
1
  import { html, LitElement, css } from 'lit'
4
2
  import { customElement, property, state } from 'lit/decorators.js'
5
3
  import { client } from '@operato/graphql'
@@ -7,8 +5,8 @@ import { notify } from '@operato/layout'
7
5
  import gql from 'graphql-tag'
8
6
  import { CommonHeaderStyles, ScrollbarStyles } from '@operato/styles'
9
7
 
10
- @customElement('kpi-value-manual-entry-form')
11
- export class KpiValueManualEntryForm extends LitElement {
8
+ @customElement('kpi-metric-value-manual-entry-form')
9
+ export class KpiMetricValueManualEntryForm extends LitElement {
12
10
  static styles = [
13
11
  CommonHeaderStyles,
14
12
  ScrollbarStyles,
@@ -18,14 +16,12 @@ export class KpiValueManualEntryForm extends LitElement {
18
16
  flex-direction: column;
19
17
  background-color: var(--md-sys-color-surface, #f4f6fa);
20
18
  }
21
-
22
19
  form {
23
20
  flex: 1;
24
21
  display: flex;
25
22
  flex-direction: column;
26
23
  overflow-y: auto;
27
24
  }
28
-
29
25
  .form-card {
30
26
  flex: 1;
31
27
  display: flex;
@@ -33,7 +29,6 @@ export class KpiValueManualEntryForm extends LitElement {
33
29
  padding: 32px 32px 0 32px;
34
30
  background: #fff;
35
31
  }
36
-
37
32
  label {
38
33
  font-weight: 500;
39
34
  margin-bottom: 4px;
@@ -41,7 +36,6 @@ export class KpiValueManualEntryForm extends LitElement {
41
36
  flex-direction: column;
42
37
  gap: 2px;
43
38
  }
44
-
45
39
  input,
46
40
  textarea {
47
41
  font-size: 1em;
@@ -50,13 +44,11 @@ export class KpiValueManualEntryForm extends LitElement {
50
44
  margin-top: 2px;
51
45
  resize: none;
52
46
  }
53
-
54
47
  .desc {
55
48
  font-size: 0.95em;
56
49
  color: #888;
57
50
  margin-top: 8px;
58
51
  }
59
-
60
52
  .footer span {
61
53
  font-size: 0.8em;
62
54
  color: var(--md-sys-color-on-surface);
@@ -66,11 +58,10 @@ export class KpiValueManualEntryForm extends LitElement {
66
58
  `
67
59
  ]
68
60
 
69
- @property({ type: Object }) kpi: any
61
+ @property({ type: Object }) metric: any
70
62
  @state() valueDate = new Date().toISOString().slice(0, 10)
71
63
  @state() value = ''
72
- @state() groupId = ''
73
- @state() groupType = ''
64
+ @state() group = ''
74
65
  @state() meta = ''
75
66
  @state() loading = false
76
67
 
@@ -78,22 +69,18 @@ export class KpiValueManualEntryForm extends LitElement {
78
69
  return html`
79
70
  <form style="display:flex;flex-direction:column;height:100%;">
80
71
  <div class="form-card">
81
- <div style="font-size:1.1em;font-weight:bold;margin-bottom:16px;"><b>KPI:</b> ${this.kpi?.name}</div>
72
+ <div style="font-size:1.1em;font-weight:bold;margin-bottom:16px;"><b>Metric:</b> ${this.metric?.name}</div>
82
73
  <label>
83
- 실적일자
74
+ 날짜
84
75
  <input type="date" .value=${this.valueDate} @input=${e => (this.valueDate = e.target.value)} required />
85
76
  </label>
86
77
  <label>
87
- 실적값
78
+
88
79
  <input type="number" .value=${this.value} @input=${e => (this.value = e.target.value)} required />
89
80
  </label>
90
81
  <label>
91
- 그룹ID (선택)
92
- <input type="text" .value=${this.groupId} @input=${e => (this.groupId = e.target.value)} />
93
- </label>
94
- <label>
95
- 그룹유형 (선택)
96
- <input type="text" .value=${this.groupType} @input=${e => (this.groupType = e.target.value)} />
82
+ 그룹 (선택)
83
+ <input type="text" .value=${this.group} @input=${e => (this.group = e.target.value)} />
97
84
  </label>
98
85
  <label>
99
86
  메타정보 (선택, JSON)
@@ -102,7 +89,6 @@ export class KpiValueManualEntryForm extends LitElement {
102
89
  <div class="desc">입력방식: MANUAL, Source: MANUAL (자동 지정)</div>
103
90
  </div>
104
91
  </form>
105
-
106
92
  <div class="footer">
107
93
  <div filler></div>
108
94
  <button type="button" ?disabled=${this.loading} done @click=${this.save}><md-icon>save</md-icon>저장</button>
@@ -125,25 +111,15 @@ export class KpiValueManualEntryForm extends LitElement {
125
111
  try {
126
112
  await client.mutate({
127
113
  mutation: gql`
128
- mutation (
129
- $kpiId: ID!
130
- $valueDate: String!
131
- $value: Float!
132
- $groupId: String
133
- $groupType: String
134
- $meta: Object
135
- ) {
136
- createKpiValue(
137
- kpiValue: {
138
- kpiId: $kpiId
114
+ mutation ($metricId: ID!, $valueDate: String!, $value: Float!, $group: String, $meta: Object) {
115
+ createKpiMetricValue(
116
+ metricValue: {
117
+ metricId: $metricId
139
118
  valueDate: $valueDate
140
119
  value: $value
141
- version: 1
142
- groupId: $groupId
143
- groupType: $groupType
144
- inputType: MANUAL
145
- source: "MANUAL"
120
+ group: $group
146
121
  meta: $meta
122
+ periodType: DAY
147
123
  }
148
124
  ) {
149
125
  id
@@ -151,17 +127,15 @@ export class KpiValueManualEntryForm extends LitElement {
151
127
  }
152
128
  `,
153
129
  variables: {
154
- kpiId: this.kpi.id,
130
+ metricId: this.metric.id,
155
131
  valueDate: this.valueDate,
156
132
  value: parseFloat(this.value),
157
- groupId: this.groupId || undefined,
158
- groupType: this.groupType || undefined,
133
+ group: this.group || undefined,
159
134
  meta: parsedMeta
160
135
  }
161
136
  })
162
- notify({ message: '실적값이 저장되었습니다.' })
137
+ notify({ message: 'Metric 값이 저장되었습니다.' })
163
138
  this.dispatchEvent(new CustomEvent('saved', { bubbles: true, composed: true }))
164
- // 팝업 닫기 로직
165
139
  let el: any = this.parentElement
166
140
  while (el) {
167
141
  if (typeof el.close === 'function') {
@@ -1,6 +1,6 @@
1
1
  import '@material/web/icon/icon.js'
2
2
  import '@operato/data-grist'
3
- import './kpi-value-manual-entry-form.js'
3
+ import './kpi-metric-value-manual-entry-form.js'
4
4
 
5
5
  import { css, html } from 'lit'
6
6
  import { customElement, property, query, state } from 'lit/decorators.js'
@@ -13,8 +13,8 @@ import { PageView, store } from '@operato/shell'
13
13
  import { CommonHeaderStyles, ScrollbarStyles } from '@operato/styles'
14
14
  import gql from 'graphql-tag'
15
15
 
16
- @customElement('kpi-value-manual-entry-page')
17
- export class KpiValueManualEntryPage extends connect(store)(localize(i18next)(PageView)) {
16
+ @customElement('kpi-metric-value-manual-entry-page')
17
+ export class KpiMetricValueManualEntryPage extends connect(store)(localize(i18next)(PageView)) {
18
18
  static styles = [
19
19
  ScrollbarStyles,
20
20
  CommonHeaderStyles,
@@ -32,7 +32,7 @@ export class KpiValueManualEntryPage extends connect(store)(localize(i18next)(Pa
32
32
 
33
33
  get context() {
34
34
  return {
35
- title: i18next.t('title.kpi value manual entry'),
35
+ title: i18next.t('title.kpi metric value manual entry'),
36
36
  search: {
37
37
  handler: (search: string) => {
38
38
  this.grist.searchText = search
@@ -44,12 +44,12 @@ export class KpiValueManualEntryPage extends connect(store)(localize(i18next)(Pa
44
44
  this.grist.toggleHeadroom()
45
45
  }
46
46
  },
47
- help: 'kpi/kpi-value'
47
+ help: 'kpi/kpi-metric-value'
48
48
  }
49
49
  }
50
50
 
51
51
  @state() private gristConfig: any
52
- @state() private kpis: any[] = []
52
+ @state() private metrics: any[] = []
53
53
  @query('ox-grist') private grist!: DataGrist
54
54
 
55
55
  render() {
@@ -62,7 +62,7 @@ export class KpiValueManualEntryPage extends connect(store)(localize(i18next)(Pa
62
62
  this.gristConfig = {
63
63
  list: {
64
64
  fields: ['name', 'description'],
65
- details: ['category', 'active']
65
+ details: ['periodType', 'active']
66
66
  },
67
67
  columns: [
68
68
  {
@@ -72,7 +72,7 @@ export class KpiValueManualEntryPage extends connect(store)(localize(i18next)(Pa
72
72
  iconOnly: false,
73
73
  width: 96,
74
74
  fixed: true,
75
- title: () => i18next.t('button.enter-kpi-value'),
75
+ title: () => i18next.t('button.enter-kpi-metric-value'),
76
76
  handlers: {
77
77
  click: (columns, data, column, record, rowIndex) => {
78
78
  this.openManualEntryForm(record)
@@ -96,11 +96,11 @@ export class KpiValueManualEntryPage extends connect(store)(localize(i18next)(Pa
96
96
  width: 200
97
97
  },
98
98
  {
99
- type: 'string',
100
- name: 'category',
101
- header: i18next.t('field.category'),
102
- record: { editable: false, renderer: (v, c, r) => r.category?.name },
103
- width: 120
99
+ type: 'select',
100
+ name: 'periodType',
101
+ header: '주기',
102
+ record: { editable: true, options: ['', 'DAY', 'WEEK', 'MONTH', 'QUARTER', 'YEAR', 'RANGE'] },
103
+ width: 80
104
104
  },
105
105
  {
106
106
  type: 'checkbox',
@@ -131,16 +131,13 @@ export class KpiValueManualEntryPage extends connect(store)(localize(i18next)(Pa
131
131
  const response = await client.query({
132
132
  query: gql`
133
133
  query ($filters: [Filter!], $pagination: Pagination, $sortings: [Sorting!]) {
134
- responses: kpis(filters: $filters, pagination: $pagination, sortings: $sortings) {
134
+ responses: kpiMetrics(filters: $filters, pagination: $pagination, sortings: $sortings) {
135
135
  items {
136
136
  id
137
137
  name
138
138
  description
139
+ periodType
139
140
  active
140
- category {
141
- id
142
- name
143
- }
144
141
  }
145
142
  total
146
143
  }
@@ -158,15 +155,18 @@ export class KpiValueManualEntryPage extends connect(store)(localize(i18next)(Pa
158
155
  }
159
156
  }
160
157
 
161
- openManualEntryForm(kpi) {
158
+ openManualEntryForm(metric) {
162
159
  openPopup(
163
160
  html`
164
- <kpi-value-manual-entry-form .kpi=${kpi} @saved=${() => this.grist.fetch()}></kpi-value-manual-entry-form>
161
+ <kpi-metric-value-manual-entry-form
162
+ .metric=${metric}
163
+ @saved=${() => this.grist.fetch()}
164
+ ></kpi-metric-value-manual-entry-form>
165
165
  `,
166
166
  {
167
167
  backdrop: true,
168
168
  size: 'medium',
169
- title: `${kpi.name} - KPI 실적 수동 입력`
169
+ title: `${metric.name} - Metric 수동 입력`
170
170
  }
171
171
  )
172
172
  }
@@ -130,8 +130,7 @@ export class KpiValueListPage extends connect(store)(localize(i18next)(ScopedEle
130
130
  'version',
131
131
  'valueDate',
132
132
  'value',
133
- 'groupId',
134
- 'groupType',
133
+ 'group',
135
134
  'inputType',
136
135
  'source',
137
136
  'meta',
@@ -145,8 +144,7 @@ export class KpiValueListPage extends connect(store)(localize(i18next)(ScopedEle
145
144
  'version',
146
145
  'valueDate',
147
146
  'value',
148
- 'groupId',
149
- 'groupType',
147
+ 'group',
150
148
  'inputType',
151
149
  'source',
152
150
  'meta',
@@ -169,8 +167,7 @@ export class KpiValueListPage extends connect(store)(localize(i18next)(ScopedEle
169
167
  { type: 'number', name: 'version', header: '버전', record: { editable: false }, width: 80 },
170
168
  { type: 'date', name: 'valueDate', header: '실적일', record: { editable: true }, width: 120 },
171
169
  { type: 'number', name: 'value', header: '실적값', record: { editable: true }, width: 120 },
172
- { type: 'string', name: 'groupId', header: '그룹ID', record: { editable: false }, width: 120 },
173
- { type: 'string', name: 'groupType', header: '그룹유형', record: { editable: false }, width: 100 },
170
+ { type: 'string', name: 'group', header: '그룹', record: { editable: false }, width: 120 },
174
171
  { type: 'string', name: 'inputType', header: '입력방식', record: { editable: false }, width: 100 },
175
172
  { type: 'string', name: 'source', header: '수집출처', record: { editable: false }, width: 120 },
176
173
  { type: 'object', name: 'meta', header: '메타', record: { editable: false }, width: 120 },
@@ -224,6 +221,7 @@ export class KpiValueListPage extends connect(store)(localize(i18next)(ScopedEle
224
221
  inputType
225
222
  source
226
223
  meta
224
+ group
227
225
  updater {
228
226
  id
229
227
  name