@things-factory/kpi 9.0.29 → 9.0.31
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/charts/kpi-mini-trend-chart.ts +125 -0
- package/client/charts/kpi-trend-chart.ts +163 -0
- package/client/google-map/common-google-map.ts +370 -0
- package/client/google-map/google-map-loader.ts +29 -0
- package/client/pages/kpi-dashboard/cards/kpi-level1-card.ts +248 -0
- package/client/pages/kpi-dashboard/cards/kpi-level2-comparison.ts +369 -0
- package/client/pages/kpi-dashboard/cards/kpi-level3-comparison.ts +443 -0
- package/client/pages/kpi-dashboard/components/kpi-chart-toggle.ts +72 -0
- package/client/pages/kpi-dashboard/components/kpi-left-panel.ts +399 -0
- package/client/pages/kpi-dashboard/components/kpi-map-panel.ts +302 -0
- package/client/pages/kpi-dashboard/components/kpi-region-popup.ts +355 -0
- package/client/pages/kpi-dashboard/kpi-dashboard-map.ts +243 -0
- package/client/pages/kpi-dashboard/kpi-dashboard.ts +416 -0
- package/client/route.ts +4 -0
- package/dist-client/charts/kpi-mini-trend-chart.d.ts +14 -0
- package/dist-client/charts/kpi-mini-trend-chart.js +148 -0
- package/dist-client/charts/kpi-mini-trend-chart.js.map +1 -0
- package/dist-client/charts/kpi-trend-chart.d.ts +25 -0
- package/dist-client/charts/kpi-trend-chart.js +186 -0
- package/dist-client/charts/kpi-trend-chart.js.map +1 -0
- package/dist-client/google-map/common-google-map.d.ts +34 -0
- package/dist-client/google-map/common-google-map.js +333 -0
- package/dist-client/google-map/common-google-map.js.map +1 -0
- package/dist-client/google-map/google-map-loader.d.ts +6 -0
- package/dist-client/google-map/google-map-loader.js +22 -0
- package/dist-client/google-map/google-map-loader.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.d.ts +17 -0
- package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js +279 -0
- package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.d.ts +19 -0
- package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js +385 -0
- package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.d.ts +23 -0
- package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js +465 -0
- package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.d.ts +8 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.js +78 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.d.ts +22 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js +404 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.d.ts +28 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js +298 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.d.ts +23 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js +368 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.d.ts +29 -0
- package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js +271 -0
- package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js.map +1 -0
- package/dist-client/pages/kpi-dashboard/kpi-dashboard.d.ts +21 -0
- package/dist-client/pages/kpi-dashboard/kpi-dashboard.js +398 -0
- package/dist-client/pages/kpi-dashboard/kpi-dashboard.js.map +1 -1
- package/dist-client/route.d.ts +1 -1
- package/dist-client/route.js +3 -0
- package/dist-client/route.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/index.d.ts +1 -0
- package/dist-server/index.js +1 -0
- package/dist-server/index.js.map +1 -1
- package/dist-server/migrations/index.d.ts +1 -0
- package/dist-server/migrations/index.js +12 -0
- package/dist-server/migrations/index.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/server/index.ts +1 -0
- package/server/migrations/index.ts +9 -0
- package/things-factory.config.js +2 -1
- package/translations/en.json +1 -0
- package/translations/ja.json +1 -0
- package/translations/ko.json +1 -0
- package/translations/ms.json +1 -0
- package/translations/zh.json +1 -0
|
@@ -15,6 +15,10 @@ import './kpi-value-entry';
|
|
|
15
15
|
import './kpi-alert-panel';
|
|
16
16
|
import '../../charts/kpi-radar-chart';
|
|
17
17
|
import '../../charts/kpi-boxplot-chart';
|
|
18
|
+
// 3레벨 KPI 플로팅 컴포넌트들
|
|
19
|
+
import './cards/kpi-level1-card';
|
|
20
|
+
import './cards/kpi-level2-comparison';
|
|
21
|
+
import './cards/kpi-level3-comparison';
|
|
18
22
|
let KpiDashboardPage = class KpiDashboardPage extends PageView {
|
|
19
23
|
constructor() {
|
|
20
24
|
super(...arguments);
|
|
@@ -25,6 +29,10 @@ let KpiDashboardPage = class KpiDashboardPage extends PageView {
|
|
|
25
29
|
this.showHistoryModal = false;
|
|
26
30
|
this.modalHistories = [];
|
|
27
31
|
this.modalKpiName = '';
|
|
32
|
+
this.kpiStatistics = []; // 실제 KPI 통계 데이터
|
|
33
|
+
this.statisticsLoading = true; // 통계 데이터 로딩 상태
|
|
34
|
+
this.selectedPeriodType = 'MONTH'; // 선택된 기간 타입 (MONTH로 고정)
|
|
35
|
+
this.selectedValueDate = ''; // 선택된 값 날짜
|
|
28
36
|
}
|
|
29
37
|
static { this.styles = [
|
|
30
38
|
ScrollbarStyles,
|
|
@@ -40,6 +48,7 @@ let KpiDashboardPage = class KpiDashboardPage extends PageView {
|
|
|
40
48
|
}
|
|
41
49
|
.sample-charts-section {
|
|
42
50
|
display: flex;
|
|
51
|
+
flex-direction: column;
|
|
43
52
|
gap: 40px;
|
|
44
53
|
margin-bottom: 48px;
|
|
45
54
|
align-items: flex-start;
|
|
@@ -227,13 +236,192 @@ let KpiDashboardPage = class KpiDashboardPage extends PageView {
|
|
|
227
236
|
get sampleCurrentGroup() {
|
|
228
237
|
return 'A';
|
|
229
238
|
}
|
|
239
|
+
// 현재 월을 YYYY-MM 형식으로 반환
|
|
240
|
+
get currentMonth() {
|
|
241
|
+
const now = new Date();
|
|
242
|
+
const year = now.getFullYear();
|
|
243
|
+
const month = String(now.getMonth() + 1).padStart(2, '0');
|
|
244
|
+
return `${year}-${month}`;
|
|
245
|
+
}
|
|
246
|
+
// 필터링된 KPI 통계 데이터
|
|
247
|
+
get filteredKpiStatistics() {
|
|
248
|
+
if (!this.kpiStatistics || this.kpiStatistics.length === 0)
|
|
249
|
+
return [];
|
|
250
|
+
return this.kpiStatistics.filter(stat => stat.periodType === 'MONTH' && stat.valueDate === this.selectedValueDate);
|
|
251
|
+
}
|
|
252
|
+
// 사용 가능한 기간 타입들 (MONTH로 고정)
|
|
253
|
+
get availablePeriodTypes() {
|
|
254
|
+
return ['MONTH'];
|
|
255
|
+
}
|
|
256
|
+
// MONTH 기간에 사용 가능한 날짜들
|
|
257
|
+
get availableValueDates() {
|
|
258
|
+
if (!this.kpiStatistics || this.kpiStatistics.length === 0)
|
|
259
|
+
return [];
|
|
260
|
+
const valueDates = new Set();
|
|
261
|
+
this.kpiStatistics.forEach(stat => {
|
|
262
|
+
if (stat.periodType === 'MONTH' && stat.valueDate) {
|
|
263
|
+
valueDates.add(stat.valueDate);
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
return Array.from(valueDates).sort().reverse(); // 최신 날짜부터 정렬
|
|
267
|
+
}
|
|
268
|
+
// 통계 요약 정보
|
|
269
|
+
get statisticsSummary() {
|
|
270
|
+
const filteredStats = this.filteredKpiStatistics;
|
|
271
|
+
if (filteredStats.length === 0)
|
|
272
|
+
return null;
|
|
273
|
+
const totalKpis = filteredStats.length;
|
|
274
|
+
const categories = new Set(filteredStats.map(s => s.kpi?.category?.name).filter(Boolean));
|
|
275
|
+
const totalCategories = categories.size;
|
|
276
|
+
const means = filteredStats.map(s => s.mean || 0).filter(v => v > 0);
|
|
277
|
+
const medians = filteredStats.map(s => s.median || 0).filter(v => v > 0);
|
|
278
|
+
const stdDevs = filteredStats.map(s => s.standardDeviation || 0).filter(v => v > 0);
|
|
279
|
+
const avgMean = means.length > 0 ? means.reduce((a, b) => a + b, 0) / means.length : 0;
|
|
280
|
+
const avgMedian = medians.length > 0 ? medians.reduce((a, b) => a + b, 0) / medians.length : 0;
|
|
281
|
+
const avgStdDev = stdDevs.length > 0 ? stdDevs.reduce((a, b) => a + b, 0) / stdDevs.length : 0;
|
|
282
|
+
return {
|
|
283
|
+
totalKpis,
|
|
284
|
+
totalCategories,
|
|
285
|
+
avgMean: avgMean.toFixed(2),
|
|
286
|
+
avgMedian: avgMedian.toFixed(2),
|
|
287
|
+
avgStdDev: avgStdDev.toFixed(2),
|
|
288
|
+
periodType: this.selectedPeriodType,
|
|
289
|
+
valueDate: this.selectedValueDate
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
// 기간 타입 변경 핸들러 (사용하지 않음 - MONTH로 고정)
|
|
293
|
+
_onPeriodTypeChange(event) {
|
|
294
|
+
// MONTH로 고정되어 있으므로 변경하지 않음
|
|
295
|
+
}
|
|
296
|
+
// 날짜 변경 핸들러
|
|
297
|
+
_onValueDateChange(event) {
|
|
298
|
+
const target = event.target;
|
|
299
|
+
this.selectedValueDate = target.value;
|
|
300
|
+
}
|
|
301
|
+
// 실제 KPI 통계 데이터를 기반으로 한 레이더 차트 데이터
|
|
302
|
+
get realKpiRadarData() {
|
|
303
|
+
const filteredStats = this.filteredKpiStatistics;
|
|
304
|
+
if (filteredStats.length === 0)
|
|
305
|
+
return [];
|
|
306
|
+
// 카테고리별로 통계 데이터 그룹화
|
|
307
|
+
const categoryStats = new Map();
|
|
308
|
+
filteredStats.forEach(stat => {
|
|
309
|
+
if (stat.kpi?.category?.name) {
|
|
310
|
+
const categoryName = stat.kpi.category.name;
|
|
311
|
+
if (!categoryStats.has(categoryName)) {
|
|
312
|
+
categoryStats.set(categoryName, []);
|
|
313
|
+
}
|
|
314
|
+
categoryStats.get(categoryName).push(stat);
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
// 각 카테고리별로 평균, 중앙값, 표준편차 계산
|
|
318
|
+
const result = [];
|
|
319
|
+
const categories = Array.from(categoryStats.keys());
|
|
320
|
+
categories.forEach(category => {
|
|
321
|
+
const stats = categoryStats.get(category);
|
|
322
|
+
const means = stats.map(s => s.mean || 0).filter(v => v > 0);
|
|
323
|
+
const medians = stats.map(s => s.median || 0).filter(v => v > 0);
|
|
324
|
+
const stdDevs = stats.map(s => s.standardDeviation || 0).filter(v => v > 0);
|
|
325
|
+
if (means.length > 0) {
|
|
326
|
+
result.push({ group: '평균', category, value: means.reduce((a, b) => a + b, 0) / means.length });
|
|
327
|
+
}
|
|
328
|
+
if (medians.length > 0) {
|
|
329
|
+
result.push({ group: '중앙값', category, value: medians.reduce((a, b) => a + b, 0) / medians.length });
|
|
330
|
+
}
|
|
331
|
+
if (stdDevs.length > 0) {
|
|
332
|
+
result.push({ group: '표준편차', category, value: stdDevs.reduce((a, b) => a + b, 0) / stdDevs.length });
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
return result;
|
|
336
|
+
}
|
|
337
|
+
get realKpiRadarCategories() {
|
|
338
|
+
const filteredStats = this.filteredKpiStatistics;
|
|
339
|
+
if (filteredStats.length === 0)
|
|
340
|
+
return [];
|
|
341
|
+
const categories = new Set();
|
|
342
|
+
filteredStats.forEach(stat => {
|
|
343
|
+
if (stat.kpi?.category?.name) {
|
|
344
|
+
categories.add(stat.kpi.category.name);
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
return Array.from(categories);
|
|
348
|
+
}
|
|
349
|
+
get realKpiRadarGroups() {
|
|
350
|
+
return ['평균', '중앙값', '표준편차'];
|
|
351
|
+
}
|
|
352
|
+
// 실제 KPI 통계 데이터를 기반으로 한 박스플롯 데이터
|
|
353
|
+
get realKpiBoxplotData() {
|
|
354
|
+
const filteredStats = this.filteredKpiStatistics;
|
|
355
|
+
if (filteredStats.length === 0)
|
|
356
|
+
return [];
|
|
357
|
+
// 카테고리별로 통계 데이터 그룹화
|
|
358
|
+
const categoryStats = new Map();
|
|
359
|
+
filteredStats.forEach(stat => {
|
|
360
|
+
if (stat.kpi?.category?.name) {
|
|
361
|
+
const categoryName = stat.kpi.category.name;
|
|
362
|
+
if (!categoryStats.has(categoryName)) {
|
|
363
|
+
categoryStats.set(categoryName, []);
|
|
364
|
+
}
|
|
365
|
+
categoryStats.get(categoryName).push(stat);
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
const result = [];
|
|
369
|
+
const categories = Array.from(categoryStats.keys());
|
|
370
|
+
categories.forEach(category => {
|
|
371
|
+
const stats = categoryStats.get(category);
|
|
372
|
+
// 각 KPI의 통계값들을 수집
|
|
373
|
+
const allMeans = stats.map(s => s.mean || 0).filter(v => v > 0);
|
|
374
|
+
const allMedians = stats.map(s => s.median || 0).filter(v => v > 0);
|
|
375
|
+
const allMins = stats.map(s => s.minimum || 0).filter(v => v > 0);
|
|
376
|
+
const allMaxs = stats.map(s => s.maximum || 0).filter(v => v > 0);
|
|
377
|
+
const allQ1s = stats.map(s => s.percentile25 || 0).filter(v => v > 0);
|
|
378
|
+
const allQ3s = stats.map(s => s.percentile75 || 0).filter(v => v > 0);
|
|
379
|
+
if (allMeans.length > 0) {
|
|
380
|
+
const sortedMeans = [...allMeans].sort((a, b) => a - b);
|
|
381
|
+
const min = sortedMeans[0];
|
|
382
|
+
const max = sortedMeans[sortedMeans.length - 1];
|
|
383
|
+
const mean = allMeans.reduce((a, b) => a + b, 0) / allMeans.length;
|
|
384
|
+
const median = allMedians.length > 0 ? allMedians.reduce((a, b) => a + b, 0) / allMedians.length : mean;
|
|
385
|
+
const q1 = sortedMeans[Math.floor(sortedMeans.length / 4)];
|
|
386
|
+
const q3 = sortedMeans[Math.floor((sortedMeans.length * 3) / 4)];
|
|
387
|
+
result.push({
|
|
388
|
+
group: category,
|
|
389
|
+
min,
|
|
390
|
+
q1,
|
|
391
|
+
median,
|
|
392
|
+
q3,
|
|
393
|
+
max,
|
|
394
|
+
mean,
|
|
395
|
+
value: mean // 현재 값으로 평균 사용
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
return result;
|
|
400
|
+
}
|
|
401
|
+
get realKpiBoxplotGroups() {
|
|
402
|
+
const filteredStats = this.filteredKpiStatistics;
|
|
403
|
+
if (filteredStats.length === 0)
|
|
404
|
+
return [];
|
|
405
|
+
const categories = new Set();
|
|
406
|
+
filteredStats.forEach(stat => {
|
|
407
|
+
if (stat.kpi?.category?.name) {
|
|
408
|
+
categories.add(stat.kpi.category.name);
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
return Array.from(categories);
|
|
412
|
+
}
|
|
413
|
+
get realKpiCurrentGroup() {
|
|
414
|
+
return '평균';
|
|
415
|
+
}
|
|
230
416
|
connectedCallback() {
|
|
231
417
|
super.connectedCallback();
|
|
232
418
|
this.fetchCategories();
|
|
419
|
+
this.fetchKpiStatistics();
|
|
233
420
|
}
|
|
234
421
|
pageUpdated(changes, lifecycle) {
|
|
235
422
|
if (this.active) {
|
|
236
423
|
this.fetchCategories();
|
|
424
|
+
this.fetchKpiStatistics();
|
|
237
425
|
}
|
|
238
426
|
}
|
|
239
427
|
async fetchCategories() {
|
|
@@ -291,6 +479,71 @@ let KpiDashboardPage = class KpiDashboardPage extends PageView {
|
|
|
291
479
|
this.loading = false;
|
|
292
480
|
}
|
|
293
481
|
}
|
|
482
|
+
async fetchKpiStatistics() {
|
|
483
|
+
this.statisticsLoading = true;
|
|
484
|
+
try {
|
|
485
|
+
const response = await client.query({
|
|
486
|
+
query: gql `
|
|
487
|
+
query {
|
|
488
|
+
kpiStatistics {
|
|
489
|
+
items {
|
|
490
|
+
id
|
|
491
|
+
valueDate
|
|
492
|
+
periodType
|
|
493
|
+
count
|
|
494
|
+
sum
|
|
495
|
+
range
|
|
496
|
+
mean
|
|
497
|
+
median
|
|
498
|
+
minimum
|
|
499
|
+
maximum
|
|
500
|
+
standardDeviation
|
|
501
|
+
variance
|
|
502
|
+
percentile25
|
|
503
|
+
percentile75
|
|
504
|
+
iqr
|
|
505
|
+
lowerFence
|
|
506
|
+
upperFence
|
|
507
|
+
additionalStatistics
|
|
508
|
+
metadata
|
|
509
|
+
kpi {
|
|
510
|
+
id
|
|
511
|
+
name
|
|
512
|
+
unit
|
|
513
|
+
category {
|
|
514
|
+
id
|
|
515
|
+
name
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
`
|
|
522
|
+
});
|
|
523
|
+
this.kpiStatistics = response.data.kpiStatistics.items || [];
|
|
524
|
+
// 현재 월을 기본값으로 설정
|
|
525
|
+
const currentMonth = this.currentMonth;
|
|
526
|
+
const availableDates = this.availableValueDates;
|
|
527
|
+
if (availableDates.includes(currentMonth)) {
|
|
528
|
+
// 현재 월 데이터가 있으면 현재 월로 설정
|
|
529
|
+
this.selectedValueDate = currentMonth;
|
|
530
|
+
}
|
|
531
|
+
else if (availableDates.length > 0) {
|
|
532
|
+
// 현재 월 데이터가 없으면 가장 최근 데이터로 설정
|
|
533
|
+
this.selectedValueDate = availableDates[0];
|
|
534
|
+
}
|
|
535
|
+
else {
|
|
536
|
+
// 데이터가 없으면 현재 월로 설정
|
|
537
|
+
this.selectedValueDate = currentMonth;
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
catch (e) {
|
|
541
|
+
console.error('KPI 통계 데이터를 불러오지 못했습니다:', e);
|
|
542
|
+
}
|
|
543
|
+
finally {
|
|
544
|
+
this.statisticsLoading = false;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
294
547
|
async openHistoryModal(kpi) {
|
|
295
548
|
// 전체 이력 fetch (limit 없이)
|
|
296
549
|
try {
|
|
@@ -486,6 +739,135 @@ let KpiDashboardPage = class KpiDashboardPage extends PageView {
|
|
|
486
739
|
</div>
|
|
487
740
|
</div>
|
|
488
741
|
</div>
|
|
742
|
+
|
|
743
|
+
<!-- 실제 KPI 통계 차트 섹션 -->
|
|
744
|
+
<div class="sample-charts-section">
|
|
745
|
+
<!-- 통계 필터 및 요약 -->
|
|
746
|
+
<div style="margin-bottom: 24px; background: #f8f9fa; padding: 16px; border-radius: 8px;">
|
|
747
|
+
<div style="display: flex; gap: 16px; align-items: center; flex-wrap: wrap;">
|
|
748
|
+
<div style="display: flex; align-items: center; gap: 8px;">
|
|
749
|
+
<label style="font-weight: 500; color: #333;">기간:</label>
|
|
750
|
+
<span style="padding: 6px 12px; background: #e9ecef; border-radius: 4px; color: #495057;">
|
|
751
|
+
MONTH (월별)
|
|
752
|
+
</span>
|
|
753
|
+
</div>
|
|
754
|
+
<div style="display: flex; align-items: center; gap: 8px;">
|
|
755
|
+
<label style="font-weight: 500; color: #333;">날짜:</label>
|
|
756
|
+
<select
|
|
757
|
+
.value=${this.selectedValueDate}
|
|
758
|
+
@change=${this._onValueDateChange}
|
|
759
|
+
style="padding: 6px 12px; border: 1px solid #ddd; border-radius: 4px; background: white;"
|
|
760
|
+
>
|
|
761
|
+
${this.availableValueDates.map(date => html `<option value="${date}">${date}${date === this.currentMonth ? ' (현재)' : ''}</option>`)}
|
|
762
|
+
</select>
|
|
763
|
+
</div>
|
|
764
|
+
${this.statisticsSummary
|
|
765
|
+
? html `
|
|
766
|
+
<div style="display: flex; gap: 16px; margin-left: auto;">
|
|
767
|
+
<span style="color: #666; font-size: 0.9em;">
|
|
768
|
+
<b>KPI:</b> ${this.statisticsSummary.totalKpis}개
|
|
769
|
+
</span>
|
|
770
|
+
<span style="color: #666; font-size: 0.9em;">
|
|
771
|
+
<b>카테고리:</b> ${this.statisticsSummary.totalCategories}개
|
|
772
|
+
</span>
|
|
773
|
+
<span style="color: #666; font-size: 0.9em;">
|
|
774
|
+
<b>평균:</b> ${this.statisticsSummary.avgMean}
|
|
775
|
+
</span>
|
|
776
|
+
<span style="color: #666; font-size: 0.9em;">
|
|
777
|
+
<b>중앙값:</b> ${this.statisticsSummary.avgMedian}
|
|
778
|
+
</span>
|
|
779
|
+
<span style="color: #666; font-size: 0.9em;">
|
|
780
|
+
<b>표준편차:</b> ${this.statisticsSummary.avgStdDev}
|
|
781
|
+
</span>
|
|
782
|
+
</div>
|
|
783
|
+
`
|
|
784
|
+
: nothing}
|
|
785
|
+
</div>
|
|
786
|
+
</div>
|
|
787
|
+
|
|
788
|
+
<div class="sample-chart-card">
|
|
789
|
+
<div class="sample-chart-title">실제 KPI 통계 비교 (Radar)</div>
|
|
790
|
+
<div class="sample-chart-container">
|
|
791
|
+
${this.statisticsLoading
|
|
792
|
+
? html `<div style="display:flex;align-items:center;justify-content:center;height:100%;color:#666;">
|
|
793
|
+
통계 데이터 로딩 중...
|
|
794
|
+
</div>`
|
|
795
|
+
: this.realKpiRadarData.length > 0
|
|
796
|
+
? html `<kpi-radar-chart
|
|
797
|
+
.data=${this.realKpiRadarData}
|
|
798
|
+
.categories=${this.realKpiRadarCategories}
|
|
799
|
+
.currentGroup=${'평균'}
|
|
800
|
+
></kpi-radar-chart>`
|
|
801
|
+
: html `<div style="display:flex;align-items:center;justify-content:center;height:100%;color:#666;">
|
|
802
|
+
선택된 기간에 통계 데이터가 없습니다.
|
|
803
|
+
</div>`}
|
|
804
|
+
</div>
|
|
805
|
+
<div style="margin-top:12px;color:#666;font-size:0.98em;">
|
|
806
|
+
※ <b>실제 KPI 통계 Radar 차트</b>는 실제 KPIStatistic 데이터를 기반으로 각 카테고리별 평균, 중앙값,
|
|
807
|
+
표준편차를 비교합니다.<br />
|
|
808
|
+
<b>평균</b>은 산술평균, <b>중앙값</b>은 50분위수, <b>표준편차</b>는 데이터 분산 정도를 나타냅니다.
|
|
809
|
+
</div>
|
|
810
|
+
</div>
|
|
811
|
+
<div class="sample-chart-card">
|
|
812
|
+
<div class="sample-chart-title">실제 KPI 통계 분포 (Boxplot)</div>
|
|
813
|
+
<div class="sample-chart-container">
|
|
814
|
+
${this.statisticsLoading
|
|
815
|
+
? html `<div style="display:flex;align-items:center;justify-content:center;height:100%;color:#666;">
|
|
816
|
+
통계 데이터 로딩 중...
|
|
817
|
+
</div>`
|
|
818
|
+
: this.realKpiBoxplotData.length > 0
|
|
819
|
+
? html `<kpi-boxplot-chart
|
|
820
|
+
.data=${this.realKpiBoxplotData}
|
|
821
|
+
.groups=${this.realKpiBoxplotGroups}
|
|
822
|
+
.currentGroup=${'평균'}
|
|
823
|
+
></kpi-boxplot-chart>`
|
|
824
|
+
: html `<div style="display:flex;align-items:center;justify-content:center;height:100%;color:#666;">
|
|
825
|
+
선택된 기간에 통계 데이터가 없습니다.
|
|
826
|
+
</div>`}
|
|
827
|
+
</div>
|
|
828
|
+
<div style="margin-top:12px;color:#666;font-size:0.98em;">
|
|
829
|
+
※ <b>실제 KPI 통계 Boxplot</b>은 실제 KPIStatistic 데이터를 기반으로 각 카테고리별 통계값의 분포를
|
|
830
|
+
보여줍니다.<br />
|
|
831
|
+
각 카테고리의 <b>평균값들</b>을 기준으로 분포를 계산하여, 카테고리 간 통계적 특성을 비교할 수 있습니다.
|
|
832
|
+
</div>
|
|
833
|
+
</div>
|
|
834
|
+
</div>
|
|
835
|
+
|
|
836
|
+
<!-- 3레벨 KPI 플로팅 섹션 -->
|
|
837
|
+
<div class="sample-charts-section">
|
|
838
|
+
<div style="margin-bottom: 24px;">
|
|
839
|
+
<h2 style="font-size: 1.5rem; font-weight: bold; color: #333; text-align: center; margin-bottom: 8px;">
|
|
840
|
+
3레벨 KPI 분석
|
|
841
|
+
</h2>
|
|
842
|
+
<p style="text-align: center; color: #666; font-size: 0.95rem;">
|
|
843
|
+
실제 KPIStatistic 데이터를 기반으로 한 다층적 분석
|
|
844
|
+
</p>
|
|
845
|
+
</div>
|
|
846
|
+
|
|
847
|
+
<!-- 1레벨: 그룹 총 스코어 -->
|
|
848
|
+
<div style="margin-bottom: 32px;">
|
|
849
|
+
<h3 style="font-size: 1.2rem; font-weight: 600; color: #495057; margin-bottom: 16px;">
|
|
850
|
+
📊 1레벨: 그룹 총 스코어
|
|
851
|
+
</h3>
|
|
852
|
+
<kpi-level1-card></kpi-level1-card>
|
|
853
|
+
</div>
|
|
854
|
+
|
|
855
|
+
<!-- 2레벨: 카테고리 비교 -->
|
|
856
|
+
<div style="margin-bottom: 32px;">
|
|
857
|
+
<h3 style="font-size: 1.2rem; font-weight: 600; color: #495057; margin-bottom: 16px;">
|
|
858
|
+
📈 2레벨: 카테고리 비교 분석
|
|
859
|
+
</h3>
|
|
860
|
+
<kpi-level2-comparison></kpi-level2-comparison>
|
|
861
|
+
</div>
|
|
862
|
+
|
|
863
|
+
<!-- 3레벨: 개별 KPI 비교 -->
|
|
864
|
+
<div style="margin-bottom: 32px;">
|
|
865
|
+
<h3 style="font-size: 1.2rem; font-weight: 600; color: #495057; margin-bottom: 16px;">
|
|
866
|
+
🔍 3레벨: 개별 KPI 상세 분석
|
|
867
|
+
</h3>
|
|
868
|
+
<kpi-level3-comparison></kpi-level3-comparison>
|
|
869
|
+
</div>
|
|
870
|
+
</div>
|
|
489
871
|
${this.showHistoryModal
|
|
490
872
|
? html `
|
|
491
873
|
<div
|
|
@@ -636,6 +1018,22 @@ __decorate([
|
|
|
636
1018
|
state(),
|
|
637
1019
|
__metadata("design:type", String)
|
|
638
1020
|
], KpiDashboardPage.prototype, "modalKpiName", void 0);
|
|
1021
|
+
__decorate([
|
|
1022
|
+
state(),
|
|
1023
|
+
__metadata("design:type", Array)
|
|
1024
|
+
], KpiDashboardPage.prototype, "kpiStatistics", void 0);
|
|
1025
|
+
__decorate([
|
|
1026
|
+
state(),
|
|
1027
|
+
__metadata("design:type", Object)
|
|
1028
|
+
], KpiDashboardPage.prototype, "statisticsLoading", void 0);
|
|
1029
|
+
__decorate([
|
|
1030
|
+
state(),
|
|
1031
|
+
__metadata("design:type", String)
|
|
1032
|
+
], KpiDashboardPage.prototype, "selectedPeriodType", void 0);
|
|
1033
|
+
__decorate([
|
|
1034
|
+
state(),
|
|
1035
|
+
__metadata("design:type", String)
|
|
1036
|
+
], KpiDashboardPage.prototype, "selectedValueDate", void 0);
|
|
639
1037
|
KpiDashboardPage = __decorate([
|
|
640
1038
|
customElement('kpi-dashboard')
|
|
641
1039
|
], KpiDashboardPage);
|