@things-factory/kpi 9.0.18 → 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 (134) hide show
  1. package/client/bootstrap.ts +8 -0
  2. package/client/pages/kpi/kpi-list-page.ts +91 -11
  3. package/client/pages/kpi-category/kpi-category-list-page.ts +80 -8
  4. package/client/pages/kpi-history/kpi-history-list-page.ts +1 -1
  5. package/client/pages/kpi-metric/kpi-metric-list-page.ts +31 -7
  6. package/client/pages/kpi-metric-value/kpi-metric-value-importer.ts +65 -0
  7. package/client/pages/kpi-metric-value/kpi-metric-value-list-page.ts +299 -0
  8. package/client/pages/{kpi-value/kpi-value-manual-entry-form.ts → kpi-metric-value/kpi-metric-value-manual-entry-form.ts} +18 -44
  9. package/client/pages/{kpi-value/kpi-value-manual-entry-page.ts → kpi-metric-value/kpi-metric-value-manual-entry-page.ts} +21 -21
  10. package/client/pages/kpi-value/kpi-value-list-page.ts +4 -6
  11. package/client/route.ts +6 -2
  12. package/dist-client/bootstrap.d.ts +2 -0
  13. package/dist-client/bootstrap.js +7 -0
  14. package/dist-client/bootstrap.js.map +1 -0
  15. package/dist-client/pages/kpi/kpi-list-page.d.ts +6 -0
  16. package/dist-client/pages/kpi/kpi-list-page.js +94 -11
  17. package/dist-client/pages/kpi/kpi-list-page.js.map +1 -1
  18. package/dist-client/pages/kpi-category/kpi-category-list-page.d.ts +5 -0
  19. package/dist-client/pages/kpi-category/kpi-category-list-page.js +83 -8
  20. package/dist-client/pages/kpi-category/kpi-category-list-page.js.map +1 -1
  21. package/dist-client/pages/kpi-history/kpi-history-list-page.js +1 -1
  22. package/dist-client/pages/kpi-history/kpi-history-list-page.js.map +1 -1
  23. package/dist-client/pages/kpi-metric/kpi-metric-list-page.js +29 -5
  24. package/dist-client/pages/kpi-metric/kpi-metric-list-page.js.map +1 -1
  25. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.d.ts +23 -0
  26. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js +75 -0
  27. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js.map +1 -0
  28. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.d.ts +61 -0
  29. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js +301 -0
  30. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js.map +1 -0
  31. 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
  32. package/dist-client/pages/{kpi-value/kpi-value-manual-entry-form.js → kpi-metric-value/kpi-metric-value-manual-entry-form.js} +27 -56
  33. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js.map +1 -0
  34. 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
  35. package/dist-client/pages/{kpi-value/kpi-value-manual-entry-page.js → kpi-metric-value/kpi-metric-value-manual-entry-page.js} +28 -28
  36. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js.map +1 -0
  37. package/dist-client/pages/kpi-value/kpi-value-list-page.js +4 -6
  38. package/dist-client/pages/kpi-value/kpi-value-list-page.js.map +1 -1
  39. package/dist-client/route.d.ts +1 -1
  40. package/dist-client/route.js +5 -2
  41. package/dist-client/route.js.map +1 -1
  42. package/dist-client/tsconfig.tsbuildinfo +1 -1
  43. package/dist-server/service/index.d.ts +4 -2
  44. package/dist-server/service/index.js +6 -1
  45. package/dist-server/service/index.js.map +1 -1
  46. package/dist-server/service/kpi/aggregate-kpi.js +5 -7
  47. package/dist-server/service/kpi/aggregate-kpi.js.map +1 -1
  48. package/dist-server/service/kpi/kpi-history.d.ts +3 -1
  49. package/dist-server/service/kpi/kpi-history.js +10 -0
  50. package/dist-server/service/kpi/kpi-history.js.map +1 -1
  51. package/dist-server/service/kpi/kpi-mutation.js +1 -1
  52. package/dist-server/service/kpi/kpi-mutation.js.map +1 -1
  53. package/dist-server/service/kpi/kpi-type.d.ts +2 -0
  54. package/dist-server/service/kpi/kpi-type.js +8 -0
  55. package/dist-server/service/kpi/kpi-type.js.map +1 -1
  56. package/dist-server/service/kpi/kpi.d.ts +9 -0
  57. package/dist-server/service/kpi/kpi.js +23 -1
  58. package/dist-server/service/kpi/kpi.js.map +1 -1
  59. package/dist-server/service/kpi-category/kpi-category-mutation.js +0 -8
  60. package/dist-server/service/kpi-category/kpi-category-mutation.js.map +1 -1
  61. package/dist-server/service/kpi-category/kpi-category-type.d.ts +4 -2
  62. package/dist-server/service/kpi-category/kpi-category-type.js +16 -8
  63. package/dist-server/service/kpi-category/kpi-category-type.js.map +1 -1
  64. package/dist-server/service/kpi-category/kpi-category.d.ts +2 -2
  65. package/dist-server/service/kpi-category/kpi-category.js +8 -8
  66. package/dist-server/service/kpi-category/kpi-category.js.map +1 -1
  67. package/dist-server/service/kpi-metric/aggregate-kpi-metric.js +31 -74
  68. package/dist-server/service/kpi-metric/aggregate-kpi-metric.js.map +1 -1
  69. package/dist-server/service/kpi-metric/kpi-metric-mutation.d.ts +1 -1
  70. package/dist-server/service/kpi-metric/kpi-metric-mutation.js +15 -28
  71. package/dist-server/service/kpi-metric/kpi-metric-mutation.js.map +1 -1
  72. package/dist-server/service/kpi-metric/kpi-metric-type.d.ts +6 -4
  73. package/dist-server/service/kpi-metric/kpi-metric-type.js +20 -12
  74. package/dist-server/service/kpi-metric/kpi-metric-type.js.map +1 -1
  75. package/dist-server/service/kpi-metric/kpi-metric.d.ts +15 -2
  76. package/dist-server/service/kpi-metric/kpi-metric.js +34 -14
  77. package/dist-server/service/kpi-metric/kpi-metric.js.map +1 -1
  78. package/dist-server/service/kpi-metric-value/index.d.ts +6 -0
  79. package/dist-server/service/kpi-metric-value/index.js +10 -0
  80. package/dist-server/service/kpi-metric-value/index.js.map +1 -0
  81. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.d.ts +11 -0
  82. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js +229 -0
  83. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js.map +1 -0
  84. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.d.ts +13 -0
  85. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js +95 -0
  86. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js.map +1 -0
  87. package/dist-server/service/kpi-metric-value/kpi-metric-value-type.d.ts +26 -0
  88. package/dist-server/service/kpi-metric-value/kpi-metric-value-type.js +112 -0
  89. package/dist-server/service/kpi-metric-value/kpi-metric-value-type.js.map +1 -0
  90. package/dist-server/service/kpi-metric-value/kpi-metric-value.d.ts +23 -0
  91. package/dist-server/service/kpi-metric-value/kpi-metric-value.js +106 -0
  92. package/dist-server/service/kpi-metric-value/kpi-metric-value.js.map +1 -0
  93. package/dist-server/service/kpi-value/kpi-value-mutation.js +1 -2
  94. package/dist-server/service/kpi-value/kpi-value-mutation.js.map +1 -1
  95. package/dist-server/service/kpi-value/kpi-value-query.js +1 -1
  96. package/dist-server/service/kpi-value/kpi-value-query.js.map +1 -1
  97. package/dist-server/service/kpi-value/kpi-value-type.d.ts +2 -4
  98. package/dist-server/service/kpi-value/kpi-value-type.js +4 -18
  99. package/dist-server/service/kpi-value/kpi-value-type.js.map +1 -1
  100. package/dist-server/service/kpi-value/kpi-value.d.ts +3 -3
  101. package/dist-server/service/kpi-value/kpi-value.js +13 -14
  102. package/dist-server/service/kpi-value/kpi-value.js.map +1 -1
  103. package/dist-server/tsconfig.tsbuildinfo +1 -1
  104. package/package.json +3 -3
  105. package/server/service/index.ts +6 -1
  106. package/server/service/kpi/aggregate-kpi.ts +5 -8
  107. package/server/service/kpi/kpi-history.ts +9 -1
  108. package/server/service/kpi/kpi-mutation.ts +1 -1
  109. package/server/service/kpi/kpi-type.ts +6 -0
  110. package/server/service/kpi/kpi.ts +21 -0
  111. package/server/service/kpi-category/kpi-category-mutation.ts +0 -10
  112. package/server/service/kpi-category/kpi-category-type.ts +12 -6
  113. package/server/service/kpi-category/kpi-category.ts +6 -6
  114. package/server/service/kpi-metric/aggregate-kpi-metric.ts +29 -69
  115. package/server/service/kpi-metric/kpi-metric-mutation.ts +15 -26
  116. package/server/service/kpi-metric/kpi-metric-type.ts +17 -12
  117. package/server/service/kpi-metric/kpi-metric.ts +32 -11
  118. package/server/service/kpi-metric-value/index.ts +7 -0
  119. package/server/service/kpi-metric-value/kpi-metric-value-mutation.ts +215 -0
  120. package/server/service/kpi-metric-value/kpi-metric-value-query.ts +60 -0
  121. package/server/service/kpi-metric-value/kpi-metric-value-type.ts +82 -0
  122. package/server/service/kpi-metric-value/kpi-metric-value.ts +91 -0
  123. package/server/service/kpi-value/kpi-value-mutation.ts +1 -2
  124. package/server/service/kpi-value/kpi-value-query.ts +1 -1
  125. package/server/service/kpi-value/kpi-value-type.ts +4 -16
  126. package/server/service/kpi-value/kpi-value.ts +14 -14
  127. package/things-factory.config.js +5 -3
  128. package/translations/en.json +8 -1
  129. package/translations/ja.json +8 -1
  130. package/translations/ko.json +9 -2
  131. package/translations/ms.json +9 -2
  132. package/translations/zh.json +8 -1
  133. package/dist-client/pages/kpi-value/kpi-value-manual-entry-form.js.map +0 -1
  134. package/dist-client/pages/kpi-value/kpi-value-manual-entry-page.js.map +0 -1
@@ -0,0 +1,215 @@
1
+ import { Resolver, Mutation, Arg, Ctx, Directive } from 'type-graphql'
2
+ import { In } from 'typeorm'
3
+ import { getRepository } from '@things-factory/shell'
4
+ import { Float } from 'type-graphql'
5
+ import { KpiPeriodType } from '../kpi/kpi'
6
+
7
+ import { KpiMetricValue } from './kpi-metric-value'
8
+ import { NewKpiMetricValue, KpiMetricValuePatch } from './kpi-metric-value-type'
9
+ import { KpiMetric } from '../kpi-metric/kpi-metric'
10
+ import { ScalarObject } from '@things-factory/shell'
11
+
12
+ function getISOWeek(date: Date): number {
13
+ const tmp = new Date(date.getTime())
14
+ tmp.setHours(0, 0, 0, 0)
15
+ tmp.setDate(tmp.getDate() + 4 - (tmp.getDay() || 7))
16
+ const yearStart = new Date(tmp.getFullYear(), 0, 1)
17
+ const weekNo = Math.ceil(((tmp.getTime() - yearStart.getTime()) / 86400000 + 1) / 7)
18
+ return weekNo
19
+ }
20
+
21
+ function getDefaultValueDate(periodType: KpiPeriodType): string {
22
+ const now = new Date()
23
+ switch (periodType) {
24
+ case KpiPeriodType.DAY:
25
+ return now.toISOString().slice(0, 10)
26
+ case KpiPeriodType.MONTH:
27
+ return now.toISOString().slice(0, 7)
28
+ case KpiPeriodType.QUARTER: {
29
+ const year = now.getFullYear()
30
+ const quarter = Math.floor(now.getMonth() / 3) + 1
31
+ return `${year}-Q${quarter}`
32
+ }
33
+ case KpiPeriodType.WEEK: {
34
+ const year = now.getFullYear()
35
+ const week = getISOWeek(now)
36
+ return `${year}-W${week}`
37
+ }
38
+ default:
39
+ return now.toISOString().slice(0, 10)
40
+ }
41
+ }
42
+
43
+ @Resolver(KpiMetricValue)
44
+ export class KpiMetricValueMutation {
45
+ @Directive('@transaction')
46
+ @Mutation(returns => KpiMetricValue, { description: 'Create a new metric value with the provided details.' })
47
+ async createKpiMetricValue(
48
+ @Arg('metricValue', { description: 'Input object containing details for the new metric value.' })
49
+ metricValue: NewKpiMetricValue,
50
+ @Ctx() context: ResolverContext
51
+ ): Promise<KpiMetricValue> {
52
+ const { domain, user, tx } = context.state
53
+
54
+ let metric = metricValue.metricId
55
+ ? await getRepository(KpiMetric).findOne({ where: { id: metricValue.metricId } })
56
+ : undefined
57
+
58
+ const entity: Partial<KpiMetricValue> = {
59
+ metric,
60
+ metricId: metricValue.metricId,
61
+ valueDate: metricValue.valueDate,
62
+ periodType: metricValue.periodType,
63
+ value: metricValue.value,
64
+ group: metricValue.group,
65
+ unit: metricValue.unit,
66
+ meta: metricValue.meta,
67
+ domain: domain,
68
+ creator: user,
69
+ updater: user
70
+ }
71
+
72
+ return await getRepository(KpiMetricValue, tx).save(entity)
73
+ }
74
+
75
+ @Directive('@transaction')
76
+ @Mutation(returns => KpiMetricValue, { description: 'Record a metric value by metric name, value, meta, and group.' })
77
+ async recordKpiMetricValue(
78
+ @Arg('metricName', { description: 'Metric code/name.' }) metricName: string,
79
+ @Arg('value', type => Float, { nullable: true, description: 'Metric value (number).' }) value: number | null,
80
+ @Arg('meta', type => ScalarObject, { nullable: true, description: 'Extended or non-numeric information (JSON).' })
81
+ meta: any,
82
+ @Arg('group', { nullable: true, description: 'Group key for this value (organization, line, user, etc.)' })
83
+ group: string | null,
84
+ @Ctx() context: ResolverContext
85
+ ): Promise<KpiMetricValue> {
86
+ const { domain, user, tx } = context.state
87
+ const metric = await getRepository(KpiMetric).findOne({ where: { name: metricName, domain: { id: domain.id } } })
88
+ if (!metric) throw new Error(`Metric not found: ${metricName}`)
89
+ const periodType = (metric.periodType as unknown as KpiPeriodType) || KpiPeriodType.DAY
90
+ const valueDateToUse = getDefaultValueDate(periodType)
91
+ if (value == null && meta == null) {
92
+ throw new Error('Either value or meta must be provided.')
93
+ }
94
+ const entity: Partial<KpiMetricValue> = {
95
+ metric,
96
+ metricId: metric.id,
97
+ value,
98
+ meta,
99
+ valueDate: valueDateToUse,
100
+ periodType,
101
+ group,
102
+ domain,
103
+ creator: user,
104
+ updater: user
105
+ }
106
+ return await getRepository(KpiMetricValue, tx).save(entity)
107
+ }
108
+
109
+ @Directive('@transaction')
110
+ @Mutation(returns => KpiMetricValue, { description: 'To modify KpiMetricValue information' })
111
+ async updateKpiMetricValue(
112
+ @Arg('id') id: string,
113
+ @Arg('patch') patch: KpiMetricValuePatch,
114
+ @Ctx() context: ResolverContext
115
+ ): Promise<KpiMetricValue> {
116
+ const { domain, user, tx } = context.state
117
+
118
+ const repository = getRepository(KpiMetricValue, tx)
119
+ const metricValue = await repository.findOne({
120
+ where: { domain: { id: domain.id }, id }
121
+ })
122
+
123
+ let metric = patch.metricId
124
+ ? await getRepository(KpiMetric).findOne({ where: { id: patch.metricId } })
125
+ : metricValue.metric
126
+
127
+ const entity: any = {
128
+ ...metricValue,
129
+ ...patch,
130
+ metric,
131
+ updater: user
132
+ }
133
+
134
+ return await repository.save(entity)
135
+ }
136
+
137
+ @Directive('@transaction')
138
+ @Mutation(returns => [KpiMetricValue], { description: "To modify multiple KpiMetricValues' information" })
139
+ async updateMultipleKpiMetricValue(
140
+ @Arg('patches', type => [KpiMetricValuePatch]) patches: KpiMetricValuePatch[],
141
+ @Ctx() context: ResolverContext
142
+ ): Promise<KpiMetricValue[]> {
143
+ const { domain, user, tx } = context.state
144
+
145
+ let results = []
146
+ const _createRecords = patches.filter((patch: any) => patch.cuFlag && patch.cuFlag.toUpperCase() === '+')
147
+ const _updateRecords = patches.filter((patch: any) => patch.cuFlag && patch.cuFlag.toUpperCase() === 'M')
148
+ const metricValueRepo = getRepository(KpiMetricValue, tx)
149
+
150
+ if (_createRecords.length > 0) {
151
+ for (let i = 0; i < _createRecords.length; i++) {
152
+ const newRecord = _createRecords[i]
153
+ const result = await metricValueRepo.save({
154
+ ...newRecord,
155
+ domain,
156
+ creator: user,
157
+ updater: user
158
+ } as any)
159
+ results.push({ ...result, cuFlag: '+' })
160
+ }
161
+ }
162
+
163
+ if (_updateRecords.length > 0) {
164
+ for (let i = 0; i < _updateRecords.length; i++) {
165
+ const updateRecord = _updateRecords[i]
166
+ const metricValue = await metricValueRepo.findOneBy({ id: updateRecord.id })
167
+ const result = await metricValueRepo.save({
168
+ ...metricValue,
169
+ ...updateRecord,
170
+ updater: user
171
+ } as any)
172
+ results.push({ ...result, cuFlag: 'M' })
173
+ }
174
+ }
175
+
176
+ return results
177
+ }
178
+
179
+ @Directive('@transaction')
180
+ @Mutation(returns => Boolean, { description: 'To delete KpiMetricValue' })
181
+ async deleteKpiMetricValue(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<boolean> {
182
+ const { domain, tx } = context.state
183
+ await getRepository(KpiMetricValue, tx).delete({ domain: { id: domain.id }, id })
184
+ return true
185
+ }
186
+
187
+ @Directive('@transaction')
188
+ @Mutation(returns => Boolean, { description: 'To delete multiple KpiMetricValues' })
189
+ async deleteKpiMetricValues(
190
+ @Arg('ids', type => [String]) ids: string[],
191
+ @Ctx() context: ResolverContext
192
+ ): Promise<boolean> {
193
+ const { domain, tx } = context.state
194
+ await getRepository(KpiMetricValue, tx).delete({
195
+ domain: { id: domain.id },
196
+ id: In(ids)
197
+ })
198
+ return true
199
+ }
200
+
201
+ @Directive('@transaction')
202
+ @Mutation(returns => Boolean, { description: 'To import multiple KpiMetricValues' })
203
+ async importKpiMetricValues(
204
+ @Arg('metricValues', type => [KpiMetricValuePatch]) metricValues: KpiMetricValuePatch[],
205
+ @Ctx() context: ResolverContext
206
+ ): Promise<boolean> {
207
+ const { domain, tx } = context.state
208
+ await Promise.all(
209
+ metricValues.map(async (metricValue: KpiMetricValuePatch) => {
210
+ await getRepository(KpiMetricValue, tx).save({ domain, ...metricValue } as any)
211
+ })
212
+ )
213
+ return true
214
+ }
215
+ }
@@ -0,0 +1,60 @@
1
+ import { Resolver, Query, Args, Arg, Ctx, FieldResolver, Root } from 'type-graphql'
2
+ import { Domain, getQueryBuilderFromListParams, getRepository, ListParam } from '@things-factory/shell'
3
+ import { User } from '@things-factory/auth-base'
4
+ import { KpiMetricValue } from './kpi-metric-value'
5
+ import { KpiMetricValueList } from './kpi-metric-value-type'
6
+ import { KpiMetric } from '../kpi-metric/kpi-metric'
7
+
8
+ @Resolver(KpiMetricValue)
9
+ export class KpiMetricValueQuery {
10
+ @Query(returns => KpiMetricValueList, { description: 'To fetch multiple KpiMetricValues' })
11
+ async kpiMetricValues(
12
+ @Args(type => ListParam) params: ListParam,
13
+ @Ctx() context: ResolverContext
14
+ ): Promise<KpiMetricValueList> {
15
+ const { domain } = context.state
16
+ const queryBuilder = getQueryBuilderFromListParams({
17
+ domain,
18
+ params,
19
+ repository: await getRepository(KpiMetricValue),
20
+ searchables: ['metricId', 'group', 'valueDate']
21
+ })
22
+ const [items, total] = await queryBuilder.getManyAndCount()
23
+ return { items, total }
24
+ }
25
+
26
+ @Query(returns => KpiMetricValue, {
27
+ nullable: true,
28
+ description: 'Fetch a single metric value by its unique identifier.'
29
+ })
30
+ async kpiMetricValue(
31
+ @Arg('id', { description: 'Unique identifier of the metric value to fetch.' }) id: string,
32
+ @Ctx() context: ResolverContext
33
+ ): Promise<KpiMetricValue | null> {
34
+ const { domain } = context.state
35
+ return await getRepository(KpiMetricValue).findOne({
36
+ where: { domain: { id: domain.id }, id }
37
+ })
38
+ }
39
+
40
+ @FieldResolver(type => Domain)
41
+ async domain(@Root() metricValue: KpiMetricValue): Promise<Domain> {
42
+ return metricValue.domainId && (await getRepository(Domain).findOneBy({ id: metricValue.domainId }))
43
+ }
44
+
45
+ @FieldResolver(type => User)
46
+ async updater(@Root() metricValue: KpiMetricValue): Promise<User> {
47
+ return metricValue.updaterId && (await getRepository(User).findOneBy({ id: metricValue.updaterId }))
48
+ }
49
+
50
+ @FieldResolver(type => User)
51
+ async creator(@Root() metricValue: KpiMetricValue): Promise<User> {
52
+ return metricValue.creatorId && (await getRepository(User).findOneBy({ id: metricValue.creatorId }))
53
+ }
54
+
55
+ @FieldResolver(type => KpiMetric)
56
+ async metric(@Root() metricValue: KpiMetricValue): Promise<KpiMetric | null> {
57
+ if (!metricValue.metricId) return null
58
+ return await getRepository(KpiMetric).findOneBy({ id: metricValue.metricId })
59
+ }
60
+ }
@@ -0,0 +1,82 @@
1
+ import { ObjectType, Field, ID, Float, InputType, Int } from 'type-graphql'
2
+ import { ScalarObject } from '@things-factory/shell'
3
+ import { KpiMetricValue } from './kpi-metric-value'
4
+ import { KpiPeriodType } from '../kpi/kpi'
5
+
6
+ @InputType({
7
+ description: 'Input type for creating a new metric value. Used in mutations to provide metric value details.'
8
+ })
9
+ export class NewKpiMetricValue {
10
+ @Field(type => ID, { description: 'ID of the metric to which this value belongs.' })
11
+ metricId: string
12
+
13
+ @Field({ description: 'Date or period for which this metric value is recorded (e.g., day, month, quarter, range).' })
14
+ valueDate: string
15
+
16
+ @Field(type => KpiPeriodType, { description: 'Aggregation period type for this metric value.' })
17
+ periodType: KpiPeriodType
18
+
19
+ @Field(type => Float, { description: 'The value for this metric and period.' })
20
+ value: number
21
+
22
+ @Field({ nullable: true, description: 'Unit of measurement for this metric value.' })
23
+ unit?: string
24
+
25
+ @Field(type => ScalarObject, {
26
+ nullable: true,
27
+ description:
28
+ 'Extended or non-numeric information related to this metric value, stored as JSON. Can include status, comments, or other metadata.'
29
+ })
30
+ meta?: any
31
+
32
+ @Field({ nullable: true, description: 'Group key for this value (organization, line, user, etc.)' })
33
+ group?: string
34
+ }
35
+
36
+ @InputType({
37
+ description: 'Input type for updating an existing metric value. Used in mutations to patch metric value details.'
38
+ })
39
+ export class KpiMetricValuePatch {
40
+ @Field(type => ID, { nullable: true, description: 'ID of the metric value to update.' })
41
+ id?: string
42
+
43
+ @Field(type => ID, { nullable: true, description: 'ID of the metric to which this value belongs.' })
44
+ metricId?: string
45
+
46
+ @Field({
47
+ nullable: true,
48
+ description: 'Date or period for which this metric value is recorded (e.g., day, month, quarter, range).'
49
+ })
50
+ valueDate?: string
51
+
52
+ @Field(type => KpiPeriodType, { nullable: true, description: 'Aggregation period type for this metric value.' })
53
+ periodType?: KpiPeriodType
54
+
55
+ @Field(type => Float, { nullable: true, description: 'The value for this metric and period.' })
56
+ value?: number
57
+
58
+ @Field({ nullable: true, description: 'Unit of measurement for this metric value.' })
59
+ unit?: string
60
+
61
+ @Field(type => ScalarObject, {
62
+ nullable: true,
63
+ description:
64
+ 'Extended or non-numeric information related to this metric value, stored as JSON. Can include status, comments, or other metadata.'
65
+ })
66
+ meta?: any
67
+
68
+ @Field({ nullable: true, description: 'Group key for this value (organization, line, user, etc.)' })
69
+ group?: string
70
+
71
+ @Field({ nullable: true, description: 'Custom flag for update operations (internal use).' })
72
+ cuFlag?: string
73
+ }
74
+
75
+ @ObjectType()
76
+ export class KpiMetricValueList {
77
+ @Field(type => [KpiMetricValue])
78
+ items: KpiMetricValue[]
79
+
80
+ @Field(type => Int)
81
+ total: number
82
+ }
@@ -0,0 +1,91 @@
1
+ import {
2
+ Entity,
3
+ PrimaryGeneratedColumn,
4
+ Column,
5
+ ManyToOne,
6
+ JoinColumn,
7
+ CreateDateColumn,
8
+ UpdateDateColumn,
9
+ RelationId
10
+ } from 'typeorm'
11
+ import { ObjectType, Field, ID, Float } from 'type-graphql'
12
+ import { Domain, ScalarObject } from '@things-factory/shell'
13
+ import { KpiMetric } from '../kpi-metric/kpi-metric'
14
+ import { KpiPeriodType } from '../kpi/kpi'
15
+ import { User } from '@things-factory/auth-base'
16
+
17
+ @Entity()
18
+ @ObjectType({ description: 'Current value for each KPI metric (can be used for both state and history).' })
19
+ export class KpiMetricValue {
20
+ @PrimaryGeneratedColumn('uuid')
21
+ @Field(type => ID)
22
+ id: string
23
+
24
+ @ManyToOne(() => Domain)
25
+ @Field(type => Domain, { nullable: true })
26
+ domain?: Domain
27
+
28
+ @RelationId((entity: KpiMetricValue) => entity.domain)
29
+ @Field({ nullable: true })
30
+ domainId?: string
31
+
32
+ @ManyToOne(() => KpiMetric, { nullable: false, onDelete: 'CASCADE' })
33
+ @JoinColumn({ name: 'metricId' })
34
+ @Field(type => KpiMetric)
35
+ metric: KpiMetric
36
+
37
+ @RelationId((entity: KpiMetricValue) => entity.metric)
38
+ @Field()
39
+ metricId: string
40
+
41
+ @Column('float')
42
+ @Field(type => Float)
43
+ value: number
44
+
45
+ @Column({ nullable: true })
46
+ @Field({ nullable: true })
47
+ unit?: string
48
+
49
+ @Column({ type: 'varchar', length: 20 })
50
+ @Field({
51
+ description:
52
+ 'Date or period for which this metric value is recorded (e.g., day: YYYY-MM-DD, month: YYYY-MM, range: YYYY-MM-DD~YYYY-MM-DD).'
53
+ })
54
+ valueDate: string
55
+
56
+ @Column({ default: 'DAY' })
57
+ @Field(type => KpiPeriodType)
58
+ periodType: KpiPeriodType
59
+
60
+ @Column({ nullable: true })
61
+ @Field({ nullable: true, description: 'Group key for this value (organization, line, user, etc.)' })
62
+ group?: string
63
+
64
+ @Column({ type: 'simple-json', nullable: true })
65
+ @Field(type => ScalarObject, { nullable: true })
66
+ meta?: any
67
+
68
+ @CreateDateColumn()
69
+ @Field({ nullable: true })
70
+ createdAt?: Date
71
+
72
+ @UpdateDateColumn()
73
+ @Field({ nullable: true })
74
+ updatedAt?: Date
75
+
76
+ @ManyToOne(() => User, { nullable: true })
77
+ @Field(type => User, { nullable: true })
78
+ creator?: User
79
+
80
+ @RelationId((entity: KpiMetricValue) => entity.creator)
81
+ @Field({ nullable: true })
82
+ creatorId?: string
83
+
84
+ @ManyToOne(() => User, { nullable: true })
85
+ @Field(type => User, { nullable: true })
86
+ updater?: User
87
+
88
+ @RelationId((entity: KpiMetricValue) => entity.updater)
89
+ @Field({ nullable: true })
90
+ updaterId?: string
91
+ }
@@ -35,8 +35,7 @@ export class KpiValueMutation {
35
35
  version: kpiValue.version,
36
36
  valueDate: kpiValue.valueDate,
37
37
  value: kpiValue.value,
38
- groupId: kpiValue.groupId,
39
- groupType: kpiValue.groupType,
38
+ group: kpiValue.group,
40
39
  source: kpiValue.source,
41
40
  domain: domain,
42
41
  creator: user,
@@ -28,7 +28,7 @@ export class KpiValueQuery {
28
28
  domain,
29
29
  params,
30
30
  repository: await getRepository(KpiValue),
31
- searchables: ['name', 'description']
31
+ searchables: ['kpi', 'group', 'valueDate']
32
32
  })
33
33
 
34
34
  const [items, total] = await queryBuilder.getManyAndCount()
@@ -27,14 +27,8 @@ export class NewKpiValue {
27
27
  })
28
28
  meta?: any
29
29
 
30
- @Field({
31
- nullable: true,
32
- description: 'ID of the group (organization, department, project, etc.) this value is associated with.'
33
- })
34
- groupId?: string
35
-
36
- @Field({ nullable: true, description: 'Type of the group (e.g., USER, DEPT, PROJECT) for this value.' })
37
- groupType?: string
30
+ @Field({ nullable: true, description: 'Group key for this value (organization, line, user, etc.)' })
31
+ group?: string
38
32
 
39
33
  @Field(type => KpiValueInputType, {
40
34
  nullable: true,
@@ -78,14 +72,8 @@ export class KpiValuePatch {
78
72
  })
79
73
  meta?: any
80
74
 
81
- @Field({
82
- nullable: true,
83
- description: 'ID of the group (organization, department, project, etc.) this value is associated with.'
84
- })
85
- groupId?: string
86
-
87
- @Field({ nullable: true, description: 'Type of the group (e.g., USER, DEPT, PROJECT) for this value.' })
88
- groupType?: string
75
+ @Field({ nullable: true, description: 'Group key for this value (organization, line, user, etc.)' })
76
+ group?: string
89
77
 
90
78
  @Field(type => KpiValueInputType, {
91
79
  nullable: true,
@@ -13,7 +13,7 @@ import { ObjectType, Field, Int, ID, registerEnumType, Float } from 'type-graphq
13
13
  import { User } from '@things-factory/auth-base'
14
14
  import { Domain, ScalarObject, json5Transformer } from '@things-factory/shell'
15
15
  import { config } from '@things-factory/env'
16
- import { Kpi } from '../kpi/kpi'
16
+ import { Kpi, KpiPeriodType } from '../kpi/kpi'
17
17
 
18
18
  const ORMCONFIG = config.get('ormconfig', {})
19
19
  const DATABASE_TYPE = ORMCONFIG.type
@@ -29,8 +29,8 @@ registerEnumType(KpiValueInputType, {
29
29
  })
30
30
 
31
31
  @Entity()
32
- @Index('ix_kpi_value_latest', ['domain', 'kpi', 'valueDate', 'groupId', 'groupType', 'version'], { unique: true })
33
- @Index('ix_kpi_value_latest_for_query', ['domain', 'kpi', 'valueDate', 'groupId', 'groupType'], { unique: false })
32
+ @Index('ix_kpi_value_latest', ['domain', 'kpi', 'valueDate', 'group', 'version'], { unique: true })
33
+ @Index('ix_kpi_value_latest_for_query', ['domain', 'kpi', 'valueDate', 'group'], { unique: false })
34
34
  @ObjectType({ description: 'Entity for KpiValue' })
35
35
  export class KpiValue {
36
36
  @PrimaryGeneratedColumn('uuid')
@@ -60,8 +60,11 @@ export class KpiValue {
60
60
  @Field(type => Int, { description: 'Version of the KPI definition at the time this value was calculated.' })
61
61
  version: number
62
62
 
63
- @Column({ type: 'date' })
64
- @Field({ description: 'Date or period for which this KPI value is recorded (e.g., day, month, quarter).' })
63
+ @Column()
64
+ @Field({
65
+ description:
66
+ 'Date or period for which this KPI value is recorded (e.g., day: YYYY-MM-DD, month: YYYY-MM, quarter: YYYY-Qn, range: YYYY-MM-DD~YYYY-MM-DD).'
67
+ })
65
68
  valueDate: string
66
69
 
67
70
  @Column('float')
@@ -69,15 +72,8 @@ export class KpiValue {
69
72
  value: number
70
73
 
71
74
  @Column({ nullable: true })
72
- @Field({
73
- nullable: true,
74
- description: 'ID of the group (organization, department, project, etc.) this value is associated with.'
75
- })
76
- groupId?: string
77
-
78
- @Column({ nullable: true })
79
- @Field({ nullable: true, description: 'Type of the group (e.g., USER, DEPT, PROJECT) for this value.' })
80
- groupType?: string
75
+ @Field({ nullable: true, description: 'Group key for this value (organization, line, user, etc.)' })
76
+ group?: string
81
77
 
82
78
  @Column({ nullable: true })
83
79
  @Field({
@@ -110,6 +106,10 @@ export class KpiValue {
110
106
  })
111
107
  meta?: any
112
108
 
109
+ @Column({ default: 'DAY' })
110
+ @Field(type => KpiPeriodType, { description: 'Aggregation period type for this KPI value.' })
111
+ periodType: KpiPeriodType
112
+
113
113
  @CreateDateColumn()
114
114
  @Field({ nullable: true, description: 'Timestamp when this KPI value record was created.' })
115
115
  createdAt?: Date
@@ -1,5 +1,5 @@
1
1
  import route from './dist-client/route'
2
- // import bootstrap from './dist-client/bootstrap'
2
+ import bootstrap from './dist-client/bootstrap'
3
3
 
4
4
  export default {
5
5
  route,
@@ -10,8 +10,10 @@ export default {
10
10
  { tagname: 'kpi-category-list-page', page: 'kpi-category-list' },
11
11
  { tagname: 'kpi-metric-list-page', page: 'kpi-metric-list' },
12
12
  { tagname: 'kpi-value-list-page', page: 'kpi-value-list' },
13
+ { tagname: 'kpi-metric-value-list-page', page: 'kpi-metric-value-list' },
13
14
  { tagname: 'kpi-value-manual-entry-page', page: 'kpi-value-manual-entry' },
15
+ { tagname: 'kpi-metric-value-manual-entry-page', page: 'kpi-metric-value-manual-entry' },
14
16
  { tagname: 'kpi-history-list-page', page: 'kpi-history-list' }
15
- ]
16
- // bootstrap
17
+ ],
18
+ bootstrap
17
19
  }
@@ -1,12 +1,19 @@
1
1
  {
2
+ "label.formula": "formula",
3
+ "label.available-variables": "available variables",
4
+ "label.operators": "operators",
5
+ "label.functions": "functions",
2
6
  "title.kpi list": "KPI List",
3
7
  "title.kpi category list": "KPI Catogory List",
4
8
  "title.kpi grade list": "KPI Grade List",
5
9
  "title.kpi metric list": "KPI Metric List",
6
10
  "title.kpi value list": "KPI Value List",
11
+ "title.kpi metric value list": "KPI Metric Value List",
7
12
  "title.kpi performance board": "KPI Performance Board",
8
13
  "title.kpi management center": "KPI Management Center",
9
14
  "title.kpi overview": "KPI Overview",
10
15
  "title.kpi value manual entry": "KPI Value Manual Entry",
11
- "button.enter-kpi-value": "Enter KPI Value"
16
+ "title.kpi metric value manual entry": "KPI Metric Value Manual Entry",
17
+ "button.enter-kpi-value": "Enter KPI Value",
18
+ "button.enter-kpi-metric-value": "Enter KPI Metric Value"
12
19
  }
@@ -1,12 +1,19 @@
1
1
  {
2
+ "label.formula": "산식",
3
+ "label.available-variables": "사용 가능한 변수",
4
+ "label.operators": "연산자",
5
+ "label.functions": "함수",
2
6
  "title.kpi list": "KPI リスト",
3
7
  "title.kpi category list": "KPI カテゴリ リスト",
4
8
  "title.kpi grade list": "KPI グレード リスト",
5
9
  "title.kpi metric list": "KPI メトリック リスト",
6
10
  "title.kpi value list": "KPI 値 リスト",
11
+ "title.kpi metric value list": "KPI メトリック 値 リスト",
7
12
  "title.kpi performance board": "KPI パフォーマンス ボード",
8
13
  "title.kpi management center": "KPI 管理 センター",
9
14
  "title.kpi overview": "KPI 概要",
10
15
  "title.kpi value manual entry": "KPI 値 手入力",
11
- "button.enter-kpi-value": "KPI 値 入力"
16
+ "title.kpi metric value manual entry": "KPI メトリック 手入力",
17
+ "button.enter-kpi-value": "KPI 値 入力",
18
+ "button.enter-kpi-metric-value": "KPI メトリック 値 入力"
12
19
  }
@@ -1,12 +1,19 @@
1
1
  {
2
+ "label.formula": "산식",
3
+ "label.available-variables": "사용 가능한 변수",
4
+ "label.operators": "연산자",
5
+ "label.functions": "함수",
2
6
  "title.kpi list": "KPI 목록",
3
7
  "title.kpi category list": "KPI 카테고리 목록",
4
8
  "title.kpi grade list": "KPI 등급 목록",
5
- "title.kpi metric list": "KPI 지표 목록",
9
+ "title.kpi metric list": "KPI 메트릭 목록",
6
10
  "title.kpi value list": "KPI 값 목록",
11
+ "title.kpi metric value list": "KPI 메트릭 값 목록",
7
12
  "title.kpi performance board": "KPI 성과 보드",
8
13
  "title.kpi management center": "KPI 관리 센터",
9
14
  "title.kpi overview": "KPI 개요",
10
15
  "title.kpi value manual entry": "KPI 값 수기 입력",
11
- "button.enter-kpi-value": "KPI 값 입력"
16
+ "title.kpi metric value manual entry": "KPI 메트릭 수기 입력",
17
+ "button.enter-kpi-value": "KPI 값 입력",
18
+ "button.enter-kpi-metric-value": "KPI 메트릭 값 입력"
12
19
  }
@@ -1,12 +1,19 @@
1
1
  {
2
+ "label.formula": "formula",
3
+ "label.available-variables": "available variables",
4
+ "label.operators": "operators",
5
+ "label.functions": "functions",
2
6
  "title.kpi list": "Senarai KPI",
3
7
  "title.kpi category list": "Senarai Kategori KPI",
4
8
  "title.kpi grade list": "Senarai Gred KPI",
5
9
  "title.kpi metric list": "Senarai Metrik KPI",
6
10
  "title.kpi value list": "Senarai Nilai KPI",
11
+ "title.kpi metric value list": "Senarai Metrik KPI",
7
12
  "title.kpi performance board": "Papan KPI",
8
13
  "title.kpi management center": "Pusat Pengurusan KPI",
9
14
  "title.kpi overview": "Pengenalan KPI",
10
- "title.kpi value manual entry": "KPI 手入力",
11
- "button.enter-kpi-value": "KPI 入力"
15
+ "title.kpi value manual entry": "KPI Nilai Manual",
16
+ "title.kpi metric value manual entry": "KPI Metrik Manual",
17
+ "button.enter-kpi-value": "KPI Nilai Manual",
18
+ "button.enter-kpi-metric-value": "KPI Metrik Manual"
12
19
  }