@dssp/dkpi 1.0.0-alpha.86 → 1.0.0-alpha.88
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/_index.html +4 -4
- package/assets/helps/index.md +1 -1
- package/assets/manifest.json +2 -2
- package/dist-client/components/kpi-boxplot-chart.d.ts +7 -0
- package/dist-client/components/kpi-boxplot-chart.js +20 -5
- package/dist-client/components/kpi-boxplot-chart.js.map +1 -1
- package/dist-client/components/kpi-lookup-chart.js +1 -17
- package/dist-client/components/kpi-lookup-chart.js.map +1 -1
- package/dist-client/components/kpi-radar-chart.js +2 -2
- package/dist-client/components/kpi-radar-chart.js.map +1 -1
- package/dist-client/components/kpi-single-boxplot-chart.d.ts +7 -0
- package/dist-client/components/kpi-single-boxplot-chart.js +27 -26
- package/dist-client/components/kpi-single-boxplot-chart.js.map +1 -1
- package/dist-client/components/kpi-step-lookup-chart.d.ts +33 -0
- package/dist-client/components/kpi-step-lookup-chart.js +181 -0
- package/dist-client/components/kpi-step-lookup-chart.js.map +1 -0
- package/dist-client/components/kpi-trend-chart.js +2 -12
- package/dist-client/components/kpi-trend-chart.js.map +1 -1
- package/dist-client/google-map/common-google-map.js +5 -1
- package/dist-client/google-map/common-google-map.js.map +1 -1
- package/dist-client/pages/kpi-admin/dssp-kpi-overview.d.ts +2 -1
- package/dist-client/pages/kpi-admin/dssp-kpi-overview.js +24 -9
- package/dist-client/pages/kpi-admin/dssp-kpi-overview.js.map +1 -1
- package/dist-client/pages/kpi-admin/kpi-system-guide.js +311 -158
- package/dist-client/pages/kpi-admin/kpi-system-guide.js.map +1 -1
- package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.d.ts +6 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js +110 -50
- package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js.map +1 -1
- package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.d.ts +0 -9
- package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js +6 -91
- package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js.map +1 -1
- package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.d.ts +2 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js +148 -68
- package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js.map +1 -1
- package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js +85 -41
- package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js.map +1 -1
- package/dist-client/pages/kpi-dashboard/kpi-dashboard.js +15 -7
- package/dist-client/pages/kpi-dashboard/kpi-dashboard.js.map +1 -1
- package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js +16 -25
- package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js.map +1 -1
- package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js +9 -13
- package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js.map +1 -1
- package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.js +9 -15
- package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.js.map +1 -1
- package/dist-client/pages/sv-project-complete.js +7 -11
- package/dist-client/pages/sv-project-complete.js.map +1 -1
- package/dist-client/pages/sv-project-completed-list.js +4 -4
- package/dist-client/pages/sv-project-completed-list.js.map +1 -1
- package/dist-client/pages/sv-project-detail.d.ts +1 -0
- package/dist-client/pages/sv-project-detail.js +94 -82
- package/dist-client/pages/sv-project-detail.js.map +1 -1
- package/dist-client/pages/sv-project-list.js +15 -12
- package/dist-client/pages/sv-project-list.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/viewparts/menu-tools.js +17 -3
- package/dist-client/viewparts/menu-tools.js.map +1 -1
- package/dist-server/service/kpi-stat/kpi-stat-query.js +20 -19
- package/dist-server/service/kpi-stat/kpi-stat-query.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/openapi/unstable.yaml +2 -2
- package/package.json +3 -3
- package/schema.graphql +20 -0
- package/translations/ko.json +2 -2
- package/views/auth-page.html +4 -4
- package/views/public/home.html +5 -5
|
@@ -39,6 +39,8 @@ let KpiRegionPopup = KpiRegionPopup_1 = class KpiRegionPopup extends LitElement
|
|
|
39
39
|
if (this.selectedRegion) {
|
|
40
40
|
this.fetchRegionKpiStats();
|
|
41
41
|
}
|
|
42
|
+
// 기간이 바뀌면 추이도 그 기간 전체 축으로 다시 그림 (데이터 없는 월 포함)
|
|
43
|
+
this.generateTrendData();
|
|
42
44
|
}
|
|
43
45
|
}
|
|
44
46
|
async fetchRegionKpiStats() {
|
|
@@ -106,43 +108,37 @@ let KpiRegionPopup = KpiRegionPopup_1 = class KpiRegionPopup extends LitElement
|
|
|
106
108
|
}
|
|
107
109
|
}
|
|
108
110
|
generateChartData() {
|
|
109
|
-
//
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
111
|
+
// kpiName 표기가 'Y1. 일정성과' / '일정성과(Y1)' 등으로 환경마다 달라 전체 문자열 매칭은 깨짐.
|
|
112
|
+
// Y1~Y6 코드만 추출해 짧은 라벨로 매핑.
|
|
113
|
+
const yLabelByCode = {
|
|
114
|
+
Y1: '일정',
|
|
115
|
+
Y2: '비용',
|
|
116
|
+
Y3: '품질',
|
|
117
|
+
Y4: '안전',
|
|
118
|
+
Y5: '환경',
|
|
119
|
+
Y6: '생산성'
|
|
117
120
|
};
|
|
121
|
+
const toCategory = (kpiName) => { var _a, _b; return yLabelByCode[(_b = (_a = (kpiName || '').match(/Y[1-6]/)) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : ''] || kpiName; };
|
|
118
122
|
// 레이더 차트와 박스플롯은 항상 전체 Y-level KPI 표시 (selectedCategory 무시)
|
|
119
123
|
if (this.selectedChartType === 'radar') {
|
|
120
|
-
// 레이더 차트용 데이터 생성
|
|
124
|
+
// 레이더 차트용 데이터 생성 — 지역 평균 단일 시리즈
|
|
125
|
+
// (전체평균 기준선은 지역 평균과 차별성이 적어 제거 → 단일 시리즈라 범례도 자동 미표시)
|
|
121
126
|
const data = [];
|
|
122
127
|
const categories = [];
|
|
123
128
|
// 카테고리 수집
|
|
124
129
|
this.regionKpiStats.forEach(stat => {
|
|
125
|
-
const category =
|
|
130
|
+
const category = toCategory(stat.kpiName);
|
|
126
131
|
if (!categories.includes(category)) {
|
|
127
132
|
categories.push(category);
|
|
128
133
|
}
|
|
129
134
|
});
|
|
130
|
-
//
|
|
131
|
-
this.totalKpiStats.forEach(stat => {
|
|
132
|
-
const category = categoryMapping[stat.kpiName] || stat.kpiName;
|
|
133
|
-
data.push({
|
|
134
|
-
org: '전체평균',
|
|
135
|
-
category,
|
|
136
|
-
value: Math.round(stat.avgVal * 100)
|
|
137
|
-
});
|
|
138
|
-
});
|
|
139
|
-
// 지역 데이터 추가
|
|
135
|
+
// 지역 데이터 추가 (박스플롯과 동일 스케일·정밀도 — Math.round 제거해 수치 일치)
|
|
140
136
|
this.regionKpiStats.forEach(stat => {
|
|
141
|
-
const category =
|
|
137
|
+
const category = toCategory(stat.kpiName);
|
|
142
138
|
data.push({
|
|
143
139
|
org: this.selectedRegion || '지역',
|
|
144
140
|
category,
|
|
145
|
-
value:
|
|
141
|
+
value: stat.avgVal * 100
|
|
146
142
|
});
|
|
147
143
|
});
|
|
148
144
|
this.chartCategories = categories.length > 0 ? categories : ['일정', '비용', '품질', '안전', '환경', '생산성'];
|
|
@@ -153,7 +149,7 @@ let KpiRegionPopup = KpiRegionPopup_1 = class KpiRegionPopup extends LitElement
|
|
|
153
149
|
const data = [];
|
|
154
150
|
const categories = [];
|
|
155
151
|
this.regionKpiStats.forEach(stat => {
|
|
156
|
-
const category =
|
|
152
|
+
const category = toCategory(stat.kpiName);
|
|
157
153
|
if (!categories.includes(category)) {
|
|
158
154
|
categories.push(category);
|
|
159
155
|
}
|
|
@@ -176,21 +172,53 @@ let KpiRegionPopup = KpiRegionPopup_1 = class KpiRegionPopup extends LitElement
|
|
|
176
172
|
if (!this.selectedRegion)
|
|
177
173
|
return;
|
|
178
174
|
// 시군구 모드인 경우 소속 광역시도의 추이 데이터 사용
|
|
179
|
-
const trendRegion = this.geoGroup
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
175
|
+
const trendRegion = this.geoGroup ? KpiRegionPopup_1.GEO_TO_METRO[this.geoGroup] || this.selectedRegion : this.selectedRegion;
|
|
176
|
+
// 해당 지역의 월별 데이터를 YYYY-MM 기준 맵으로
|
|
177
|
+
const byMonth = new Map();
|
|
178
|
+
this.monthlyTrendData.filter(d => d.geoGroup === trendRegion).forEach(d => byMonth.set(d.yearMonth, d));
|
|
179
|
+
// 선택 기간(start~end) 전체 월을 축으로 사용 — 데이터가 없는 월도 레코드(값 0)로 채워
|
|
180
|
+
// 전국 패널의 기간과 동일한 범위를 플로팅. (전체 기간 모드 등으로 start/end 가 비면 데이터 월만 사용)
|
|
181
|
+
const months = this.startYearMonth && this.endYearMonth
|
|
182
|
+
? this.buildMonthRange(this.startYearMonth, this.endYearMonth)
|
|
183
|
+
: [...byMonth.keys()].sort();
|
|
184
|
+
let prev = null;
|
|
185
|
+
this.trendData = months.map(ym => {
|
|
186
|
+
const d = byMonth.get(ym);
|
|
187
|
+
const value = d ? Math.round(d.avgVal * 100) : 0; // 20배 스케일, 데이터 없으면 0
|
|
188
|
+
// 직전 월 대비 증감 방향 — 추이 화살표/색상 기준 (값 임계치가 아니라 실제 증감)
|
|
189
|
+
const trendDir = prev === null ? 'flat' : value > prev ? 'up' : value < prev ? 'down' : 'flat';
|
|
190
|
+
prev = value;
|
|
187
191
|
return {
|
|
188
|
-
date:
|
|
189
|
-
value
|
|
190
|
-
color: value > 60 ? '#4caf50' : value > 40 ? '#ff9800' : '#f44336'
|
|
192
|
+
date: ym, // YYYY-MM 형식
|
|
193
|
+
value,
|
|
194
|
+
color: value > 60 ? '#4caf50' : value > 40 ? '#ff9800' : '#f44336',
|
|
195
|
+
trendDir
|
|
191
196
|
};
|
|
192
197
|
});
|
|
193
198
|
}
|
|
199
|
+
/** start~end(YYYY-MM) 사이 모든 월을 순서대로 반환. 잘못된 범위는 안전하게 종료. */
|
|
200
|
+
buildMonthRange(start, end) {
|
|
201
|
+
const [sy, sm] = start.split('-').map(Number);
|
|
202
|
+
const [ey, em] = end.split('-').map(Number);
|
|
203
|
+
if (!sy || !sm || !ey || !em)
|
|
204
|
+
return [];
|
|
205
|
+
const out = [];
|
|
206
|
+
let y = sy;
|
|
207
|
+
let m = sm;
|
|
208
|
+
for (let i = 0; i < 240; i++) {
|
|
209
|
+
out.push(`${y}-${String(m).padStart(2, '0')}`);
|
|
210
|
+
if (y === ey && m === em)
|
|
211
|
+
break;
|
|
212
|
+
if (y > ey || (y === ey && m > em))
|
|
213
|
+
break; // start > end 방어
|
|
214
|
+
m++;
|
|
215
|
+
if (m > 12) {
|
|
216
|
+
m = 1;
|
|
217
|
+
y++;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return out;
|
|
221
|
+
}
|
|
194
222
|
onClose() {
|
|
195
223
|
this.dispatchEvent(new CustomEvent('popup-close', {
|
|
196
224
|
bubbles: true,
|
|
@@ -229,21 +257,29 @@ let KpiRegionPopup = KpiRegionPopup_1 = class KpiRegionPopup extends LitElement
|
|
|
229
257
|
</button>
|
|
230
258
|
</div>
|
|
231
259
|
<div class="chart-container">
|
|
232
|
-
${this.
|
|
233
|
-
? html
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
`
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
260
|
+
${this.chartData.length === 0
|
|
261
|
+
? html `<div
|
|
262
|
+
style="display:flex;align-items:center;justify-content:center;height:100%;color:#888;font-size:0.9rem;"
|
|
263
|
+
>
|
|
264
|
+
선택한 기간에 데이터가 없습니다.
|
|
265
|
+
</div>`
|
|
266
|
+
: this.selectedChartType === 'boxplot'
|
|
267
|
+
? html `
|
|
268
|
+
<sv-kpi-boxplot-chart
|
|
269
|
+
.data=${this.chartData}
|
|
270
|
+
.groups=${this.chartCategories}
|
|
271
|
+
.valueKey=${'value'}
|
|
272
|
+
.valueLabel=${this.selectedRegion || '지역'}
|
|
273
|
+
.showStatus=${false}
|
|
274
|
+
></sv-kpi-boxplot-chart>
|
|
275
|
+
`
|
|
276
|
+
: html `
|
|
277
|
+
<sv-kpi-radar-chart
|
|
278
|
+
.data=${this.chartData}
|
|
279
|
+
.categories=${this.chartCategories}
|
|
280
|
+
.valueKey=${'value'}
|
|
281
|
+
></sv-kpi-radar-chart>
|
|
282
|
+
`}
|
|
247
283
|
</div>
|
|
248
284
|
</div>
|
|
249
285
|
|
|
@@ -269,14 +305,16 @@ let KpiRegionPopup = KpiRegionPopup_1 = class KpiRegionPopup extends LitElement
|
|
|
269
305
|
</tr>
|
|
270
306
|
</thead>
|
|
271
307
|
<tbody>
|
|
272
|
-
${this.trendData.map((item) => html `
|
|
308
|
+
${[...this.trendData].reverse().map((item) => html `
|
|
273
309
|
<tr style="border-bottom: 1px solid #f1f3f4;">
|
|
274
310
|
<td style="padding: 8px; font-size: 0.85rem;">${item.date}</td>
|
|
275
311
|
<td style="padding: 8px; text-align: right; font-size: 0.85rem; font-weight: 600;">
|
|
276
312
|
${item.value.toFixed(1)}
|
|
277
313
|
</td>
|
|
278
314
|
<td style="padding: 8px; text-align: center; font-size: 0.85rem;">
|
|
279
|
-
<span style="color: ${item.
|
|
315
|
+
<span style="color: ${item.trendDir === 'up' ? '#e53935' : item.trendDir === 'down' ? '#2196f3' : '#999'};">
|
|
316
|
+
${item.trendDir === 'up' ? '▲' : item.trendDir === 'down' ? '▼' : ''}
|
|
317
|
+
</span>
|
|
280
318
|
</td>
|
|
281
319
|
</tr>
|
|
282
320
|
`)}
|
|
@@ -420,23 +458,65 @@ KpiRegionPopup.styles = [
|
|
|
420
458
|
];
|
|
421
459
|
// geo_group → 광역시도 매핑 (시군구 모드에서 추이 데이터 조회용)
|
|
422
460
|
KpiRegionPopup.GEO_TO_METRO = {
|
|
423
|
-
'01': '서울특별시',
|
|
424
|
-
'
|
|
425
|
-
'
|
|
426
|
-
'
|
|
427
|
-
'
|
|
428
|
-
'
|
|
429
|
-
'
|
|
430
|
-
'
|
|
431
|
-
'
|
|
432
|
-
'
|
|
433
|
-
'
|
|
434
|
-
'
|
|
435
|
-
'
|
|
436
|
-
'
|
|
437
|
-
'
|
|
438
|
-
'
|
|
439
|
-
'
|
|
461
|
+
'01': '서울특별시',
|
|
462
|
+
'02': '서울특별시',
|
|
463
|
+
'03': '서울특별시',
|
|
464
|
+
'04': '서울특별시',
|
|
465
|
+
'05': '서울특별시',
|
|
466
|
+
'06': '서울특별시',
|
|
467
|
+
'07': '서울특별시',
|
|
468
|
+
'08': '서울특별시',
|
|
469
|
+
'10': '경기도',
|
|
470
|
+
'11': '경기도',
|
|
471
|
+
'12': '경기도',
|
|
472
|
+
'13': '경기도',
|
|
473
|
+
'14': '경기도',
|
|
474
|
+
'15': '경기도',
|
|
475
|
+
'16': '경기도',
|
|
476
|
+
'17': '경기도',
|
|
477
|
+
'18': '경기도',
|
|
478
|
+
'21': '인천광역시',
|
|
479
|
+
'22': '인천광역시',
|
|
480
|
+
'23': '인천광역시',
|
|
481
|
+
'24': '강원도',
|
|
482
|
+
'25': '강원도',
|
|
483
|
+
'26': '강원도',
|
|
484
|
+
'33': '강원도',
|
|
485
|
+
'27': '충청북도',
|
|
486
|
+
'28': '충청북도',
|
|
487
|
+
'29': '충청북도',
|
|
488
|
+
'30': '세종특별자치시',
|
|
489
|
+
'31': '충청남도',
|
|
490
|
+
'32': '충청남도',
|
|
491
|
+
'34': '대전광역시',
|
|
492
|
+
'35': '대전광역시',
|
|
493
|
+
'36': '경상북도',
|
|
494
|
+
'37': '경상북도',
|
|
495
|
+
'38': '경상북도',
|
|
496
|
+
'39': '경상북도',
|
|
497
|
+
'40': '경상북도',
|
|
498
|
+
'41': '대구광역시',
|
|
499
|
+
'42': '대구광역시',
|
|
500
|
+
'43': '대구광역시',
|
|
501
|
+
'44': '울산광역시',
|
|
502
|
+
'45': '울산광역시',
|
|
503
|
+
'46': '부산광역시',
|
|
504
|
+
'47': '부산광역시',
|
|
505
|
+
'48': '부산광역시',
|
|
506
|
+
'49': '부산광역시',
|
|
507
|
+
'50': '경상남도',
|
|
508
|
+
'51': '경상남도',
|
|
509
|
+
'52': '경상남도',
|
|
510
|
+
'53': '경상남도',
|
|
511
|
+
'54': '전라북도',
|
|
512
|
+
'55': '전라북도',
|
|
513
|
+
'56': '전라북도',
|
|
514
|
+
'57': '전라남도',
|
|
515
|
+
'58': '전라남도',
|
|
516
|
+
'59': '전라남도',
|
|
517
|
+
'61': '광주광역시',
|
|
518
|
+
'62': '광주광역시',
|
|
519
|
+
'63': '제주특별자치도'
|
|
440
520
|
};
|
|
441
521
|
__decorate([
|
|
442
522
|
property({ type: String }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kpi-region-popup.js","sourceRoot":"","sources":["../../../../client/pages/kpi-dashboard/components/kpi-region-popup.ts"],"names":[],"mappings":";;AAAA,OAAO,GAAG,MAAM,aAAa,CAAA;AAC7B,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,KAAK,CAAA;AACpD,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AAClE,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AACzC,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEjD,OAAO,wCAAwC,CAAA;AAC/C,OAAO,0CAA0C,CAAA;AACjD,OAAO,wCAAwC,CAAA;AAGxC,IAAM,cAAc,sBAApB,MAAM,cAAe,SAAQ,UAAU;IAAvC;;QAoIuB,mBAAc,GAAkB,IAAI,CAAA;QACpC,aAAQ,GAAkB,IAAI,CAAA,CAAC,mCAAmC;QAClE,qBAAgB,GAAG,QAAQ,CAAA;QAC3B,sBAAiB,GAAG,SAAS,CAAA;QAC7B,mBAAc,GAAG,GAAG,CAAA;QACpB,mBAAc,GAAG,EAAE,CAAA,CAAC,aAAa;QACjC,iBAAY,GAAG,EAAE,CAAA,CAAC,aAAa;QAChC,qBAAgB,GAAU,EAAE,CAAA,CAAC,sBAAsB;QACnD,kBAAa,GAAU,EAAE,CAAA,CAAC,kBAAkB;QAEtD,cAAS,GAAU,EAAE,CAAA;QACrB,oBAAe,GAAa,EAAE,CAAA;QAC9B,cAAS,GAAsD,EAAE,CAAA;QACjE,mBAAc,GAAU,EAAE,CAAA;IA2T7C,CAAC;IAzTC,OAAO,CAAC,iBAAmC;QACzC,IAAI,iBAAiB,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;YACnE,IAAI,CAAC,mBAAmB,EAAE,CAAA;YAC1B,IAAI,CAAC,iBAAiB,EAAE,CAAA;QAC1B,CAAC;QAED,wDAAwD;QACxD,IAAI,iBAAiB,CAAC,GAAG,CAAC,kBAAkB,CAAC,IAAI,iBAAiB,CAAC,GAAG,CAAC,kBAAkB,CAAC,EAAE,CAAC;YAC3F,IAAI,CAAC,iBAAiB,EAAE,CAAA;QAC1B,CAAC;QAED,0BAA0B;QAC1B,IAAI,iBAAiB,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,iBAAiB,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,CAAC;YACrF,IAAI,IAAI,CAAC,cAAc,EAAE,CAAC;gBACxB,IAAI,CAAC,mBAAmB,EAAE,CAAA;YAC5B,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,CAAC,mBAAmB;QACvB,IAAI,CAAC,IAAI,CAAC,cAAc;YAAE,OAAM;QAEhC,IAAI,CAAC;YACH,IAAI,cAAqB,CAAA;YAEzB,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAClB,2BAA2B;gBAC3B,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC;oBAClC,KAAK,EAAE,GAAG,CAAA;;;;;;;;;;;;;WAaT;oBACD,SAAS,EAAE;wBACT,QAAQ,EAAE,IAAI,CAAC,QAAQ;wBACvB,cAAc,EAAE,IAAI,CAAC,cAAc;wBACnC,YAAY,EAAE,IAAI,CAAC,YAAY;qBAChC;iBACF,CAAC,CAAA;gBACF,cAAc,GAAG,QAAQ,CAAC,IAAI,CAAC,wBAAwB,IAAI,EAAE,CAAA;YAC/D,CAAC;iBAAM,CAAC;gBACN,sBAAsB;gBACtB,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC;oBAClC,KAAK,EAAE,GAAG,CAAA;;;;;;;;;;;;;WAaT;oBACD,SAAS,EAAE;wBACT,SAAS,EAAE,IAAI,CAAC,cAAc;wBAC9B,cAAc,EAAE,IAAI,CAAC,cAAc;wBACnC,YAAY,EAAE,IAAI,CAAC,YAAY;qBAChC;iBACF,CAAC,CAAA;gBACF,cAAc,GAAG,QAAQ,CAAC,IAAI,CAAC,yBAAyB,IAAI,EAAE,CAAA;YAChE,CAAC;YAED,IAAI,CAAC,cAAc,GAAG,cAAc,CAAA;YACpC,IAAI,CAAC,iBAAiB,EAAE,CAAA;QAC1B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,mCAAmC,EAAE,KAAK,CAAC,CAAA;YACzD,IAAI,CAAC,cAAc,GAAG,EAAE,CAAA;YACxB,IAAI,CAAC,iBAAiB,EAAE,CAAA;QAC1B,CAAC;IACH,CAAC;IAEO,iBAAiB;QACvB,wBAAwB;QACxB,MAAM,eAAe,GAA2B;YAC9C,UAAU,EAAE,IAAI;YAChB,UAAU,EAAE,IAAI;YAChB,UAAU,EAAE,IAAI;YAChB,UAAU,EAAE,IAAI;YAChB,UAAU,EAAE,IAAI;YAChB,WAAW,EAAE,KAAK;SACnB,CAAA;QAED,2DAA2D;QAC3D,IAAI,IAAI,CAAC,iBAAiB,KAAK,OAAO,EAAE,CAAC;YACvC,gCAAgC;YAChC,MAAM,IAAI,GAA4D,EAAE,CAAA;YACxE,MAAM,UAAU,GAAa,EAAE,CAAA;YAE/B,UAAU;YACV,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;gBACjC,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,OAAO,CAAA;gBAC9D,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;oBACnC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;gBAC3B,CAAC;YACH,CAAC,CAAC,CAAA;YAEF,cAAc;YACd,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;gBAChC,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,OAAO,CAAA;gBAC9D,IAAI,CAAC,IAAI,CAAC;oBACR,GAAG,EAAE,MAAM;oBACX,QAAQ;oBACR,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC;iBACrC,CAAC,CAAA;YACJ,CAAC,CAAC,CAAA;YAEF,YAAY;YACZ,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;gBACjC,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,OAAO,CAAA;gBAC9D,IAAI,CAAC,IAAI,CAAC;oBACR,GAAG,EAAE,IAAI,CAAC,cAAc,IAAI,IAAI;oBAChC,QAAQ;oBACR,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC;iBACrC,CAAC,CAAA;YACJ,CAAC,CAAC,CAAA;YAEF,IAAI,CAAC,eAAe,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA;YACjG,IAAI,CAAC,SAAS,GAAG,IAAI,CAAA;QACvB,CAAC;aAAM,CAAC;YACN,eAAe;YACf,MAAM,IAAI,GASL,EAAE,CAAA;YACP,MAAM,UAAU,GAAa,EAAE,CAAA;YAE/B,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;gBACjC,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,OAAO,CAAA;gBAC9D,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;oBACnC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;gBAC3B,CAAC;gBACD,IAAI,CAAC,IAAI,CAAC;oBACR,GAAG,EAAE,QAAQ;oBACb,GAAG,EAAE,IAAI,CAAC,MAAM,GAAG,GAAG;oBACtB,GAAG,EAAE,IAAI,CAAC,MAAM,GAAG,GAAG;oBACtB,EAAE,EAAE,IAAI,CAAC,KAAK,GAAG,GAAG;oBACpB,EAAE,EAAE,IAAI,CAAC,KAAK,GAAG,GAAG;oBACpB,MAAM,EAAE,IAAI,CAAC,MAAM,GAAG,GAAG;oBACzB,IAAI,EAAE,IAAI,CAAC,MAAM,GAAG,GAAG;oBACvB,KAAK,EAAE,IAAI,CAAC,MAAM,GAAG,GAAG;iBACzB,CAAC,CAAA;YACJ,CAAC,CAAC,CAAA;YAEF,IAAI,CAAC,eAAe,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA;YACjG,IAAI,CAAC,SAAS,GAAG,IAAI,CAAA;QACvB,CAAC;IACH,CAAC;IAuBO,iBAAiB;QACvB,IAAI,CAAC,IAAI,CAAC,cAAc;YAAE,OAAM;QAEhC,gCAAgC;QAChC,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ;YAC/B,CAAC,CAAC,gBAAc,CAAC,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,cAAc;YACnE,CAAC,CAAC,IAAI,CAAC,cAAc,CAAA;QAEvB,MAAM,eAAe,GAAG,IAAI,CAAC,gBAAgB;aAC1C,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,WAAW,CAAC;aACvC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAA;QAEzD,IAAI,CAAC,SAAS,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;YACvC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,GAAG,GAAG,CAAC,CAAA,CAAC,UAAU;YACnD,OAAO;gBACL,IAAI,EAAE,CAAC,CAAC,SAAS,EAAE,aAAa;gBAChC,KAAK,EAAE,KAAK;gBACZ,KAAK,EAAE,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS;aACnE,CAAA;QACH,CAAC,CAAC,CAAA;IACJ,CAAC;IAEO,OAAO;QACb,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAAC,aAAa,EAAE;YAC7B,OAAO,EAAE,IAAI;YACb,QAAQ,EAAE,IAAI;SACf,CAAC,CACH,CAAA;IACH,CAAC;IAEO,iBAAiB,CAAC,IAAY;QACpC,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAA;QAC7B,IAAI,CAAC,iBAAiB,EAAE,CAAA;IAC1B,CAAC;IAED,MAAM;QACJ,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;YACzB,OAAO,OAAO,CAAA;QAChB,CAAC;QAED,OAAO,IAAI,CAAA;;mCAEoB,IAAI,CAAC,cAAc;6CACT,IAAI,CAAC,OAAO;;;;;;;;qCAQpB,IAAI,CAAC,iBAAiB,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE;uBAClE,GAAG,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,SAAS,CAAC;;;;;qCAKzB,IAAI,CAAC,iBAAiB,KAAK,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE;uBAChE,GAAG,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC;;;;;;cAM9C,IAAI,CAAC,iBAAiB,KAAK,SAAS;YACpC,CAAC,CAAC,IAAI,CAAA;;4BAEQ,IAAI,CAAC,SAAS;8BACZ,IAAI,CAAC,eAAe;gCAClB,OAAO;;iBAEtB;YACH,CAAC,CAAC,IAAI,CAAA;;4BAEQ,IAAI,CAAC,SAAS;kCACR,IAAI,CAAC,eAAe;gCACtB,OAAO;;iBAEtB;;;;;;;;;sBASK,IAAI,CAAC,SAAS;2BACT,SAAS;6BACP,CAAC;4BACF,KAAK;6BACJ,CAAC;;;;;;;;;;;;;kBAaZ,IAAI,CAAC,SAAS,CAAC,GAAG,CAClB,CAAC,IAAS,EAAE,EAAE,CAAC,IAAI,CAAA;;sEAEiC,IAAI,CAAC,IAAI;;0BAErD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;;;8CAGD,IAAI,CAAC,KAAK,OAAO,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG;;;mBAG/F,CACF;;;;;;KAMZ,CAAA;IACH,CAAC;;AA1cM,qBAAM,GAAG;IACd,eAAe;IACf,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KA8HF;CACF,AAjIY,CAiIZ;AAwLD,4CAA4C;AACpB,2BAAY,GAA2B;IAC7D,IAAI,EAAC,OAAO,EAAC,IAAI,EAAC,OAAO,EAAC,IAAI,EAAC,OAAO,EAAC,IAAI,EAAC,OAAO;IACnD,IAAI,EAAC,OAAO,EAAC,IAAI,EAAC,OAAO,EAAC,IAAI,EAAC,OAAO,EAAC,IAAI,EAAC,OAAO;IACnD,IAAI,EAAC,KAAK,EAAC,IAAI,EAAC,KAAK,EAAC,IAAI,EAAC,KAAK,EAAC,IAAI,EAAC,KAAK,EAAC,IAAI,EAAC,KAAK;IACtD,IAAI,EAAC,KAAK,EAAC,IAAI,EAAC,KAAK,EAAC,IAAI,EAAC,KAAK,EAAC,IAAI,EAAC,KAAK;IAC3C,IAAI,EAAC,OAAO,EAAC,IAAI,EAAC,OAAO,EAAC,IAAI,EAAC,OAAO;IACtC,IAAI,EAAC,KAAK,EAAC,IAAI,EAAC,KAAK,EAAC,IAAI,EAAC,KAAK,EAAC,IAAI,EAAC,KAAK;IAC3C,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,MAAM;IACnC,IAAI,EAAC,SAAS,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,MAAM;IACtC,IAAI,EAAC,OAAO,EAAC,IAAI,EAAC,OAAO;IACzB,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,MAAM;IAC3D,IAAI,EAAC,OAAO,EAAC,IAAI,EAAC,OAAO,EAAC,IAAI,EAAC,OAAO;IACtC,IAAI,EAAC,OAAO,EAAC,IAAI,EAAC,OAAO;IACzB,IAAI,EAAC,OAAO,EAAC,IAAI,EAAC,OAAO,EAAC,IAAI,EAAC,OAAO,EAAC,IAAI,EAAC,OAAO;IACnD,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,MAAM;IAC/C,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,MAAM;IACnC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,MAAM,EAAC,IAAI,EAAC,MAAM;IACnC,IAAI,EAAC,OAAO,EAAC,IAAI,EAAC,OAAO,EAAC,IAAI,EAAC,SAAS;CACzC,AAlBmC,CAkBnC;AAzM2B;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;sDAAqC;AACpC;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;gDAA+B;AAC9B;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;wDAA4B;AAC3B;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;yDAA8B;AAC7B;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;sDAAqB;AACpB;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;sDAAoB;AACnB;IAA3B,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;;oDAAkB;AAClB;IAA1B,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;;wDAA6B;AAC5B;IAA1B,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;;qDAA0B;AAEnC;IAAhB,KAAK,EAAE;;iDAA8B;AACrB;IAAhB,KAAK,EAAE;;uDAAuC;AAC9B;IAAhB,KAAK,EAAE;;iDAA0E;AACjE;IAAhB,KAAK,EAAE;;sDAAmC;AAjJhC,cAAc;IAD1B,aAAa,CAAC,kBAAkB,CAAC;GACrB,cAAc,CA4c1B","sourcesContent":["import gql from 'graphql-tag'\nimport { LitElement, html, css, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { client } from '@operato/graphql'\nimport { ScrollbarStyles } from '@operato/styles'\n\nimport '../../../components/kpi-radar-chart.js'\nimport '../../../components/kpi-boxplot-chart.js'\nimport '../../../components/kpi-trend-chart.js'\n\n@customElement('kpi-region-popup')\nexport class KpiRegionPopup extends LitElement {\n static styles = [\n ScrollbarStyles,\n css`\n :host {\n display: block;\n position: absolute;\n top: 0;\n left: 402px;\n width: 400px;\n height: 100%;\n background: #fff;\n border-right: 1px solid #e0e0e0;\n z-index: 1000;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);\n }\n .popup-content {\n padding: 20px;\n overflow-y: auto;\n flex: 1;\n }\n .popup-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 20px;\n border-bottom: 1px solid #e0e0e0;\n background: #fff;\n height: 70px;\n box-sizing: border-box;\n }\n .popup-title {\n font-size: 1.2rem;\n font-weight: bold;\n color: #333;\n }\n .popup-close {\n width: 32px;\n height: 32px;\n border: none;\n background: #fff;\n border-radius: 50%;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 1.2rem;\n color: #666;\n transition: all 0.2s;\n }\n .popup-close:hover {\n background: #e9ecef;\n color: #333;\n }\n .sub-title {\n font-size: 1rem;\n font-weight: 600;\n margin-bottom: 16px;\n color: #495057;\n }\n .chart-section {\n background: #f8f9fa;\n border-radius: 8px;\n padding: 16px;\n margin-bottom: 20px;\n }\n .chart-toggle {\n display: flex;\n gap: 8px;\n margin-bottom: 16px;\n }\n .toggle-button {\n padding: 8px 16px;\n border: 1px solid #ced4da;\n background: #fff;\n border-radius: 6px;\n cursor: pointer;\n font-size: 0.9rem;\n transition: all 0.2s;\n }\n .toggle-button.active {\n background: #667eea;\n color: white;\n border-color: #667eea;\n }\n .chart-container {\n height: 300px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: white;\n border-radius: 6px;\n border: 1px solid #e9ecef;\n }\n .period-selector {\n display: flex;\n gap: 8px;\n margin-bottom: 16px;\n }\n .period-button {\n padding: 6px 12px;\n border: 1px solid #ced4da;\n background: #fff;\n border-radius: 4px;\n cursor: pointer;\n font-size: 0.85rem;\n transition: all 0.2s;\n }\n .period-button.active {\n background: #667eea;\n color: white;\n border-color: #667eea;\n }\n .date-range-selector {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 16px;\n }\n .date-select {\n padding: 6px 12px;\n border: 1px solid #ced4da;\n border-radius: 4px;\n background: white;\n font-size: 0.9rem;\n }\n `\n ]\n\n @property({ type: String }) selectedRegion: string | null = null\n @property({ type: String }) geoGroup: string | null = null // 시군구 geo_group 코드 (있으면 시군구 쿼리 사용)\n @property({ type: String }) selectedCategory = '전체 KPI'\n @property({ type: String }) selectedChartType = 'boxplot'\n @property({ type: String }) selectedPeriod = '월'\n @property({ type: String }) startYearMonth = '' // YYYY-MM 형식\n @property({ type: String }) endYearMonth = '' // YYYY-MM 형식\n @property({ type: Array }) monthlyTrendData: any[] = [] // 부모에서 전달받은 월별 추이 데이터\n @property({ type: Array }) totalKpiStats: any[] = [] // 전체 KPI 통계 (비교용)\n\n @state() private chartData: any[] = []\n @state() private chartCategories: string[] = []\n @state() private trendData: { date: string; value: number; color?: string }[] = []\n @state() private regionKpiStats: any[] = []\n\n updated(changedProperties: Map<string, any>) {\n if (changedProperties.has('selectedRegion') && this.selectedRegion) {\n this.fetchRegionKpiStats()\n this.generateTrendData()\n }\n\n // selectedCategory 또는 monthlyTrendData가 변경되면 추이 데이터 재생성\n if (changedProperties.has('selectedCategory') || changedProperties.has('monthlyTrendData')) {\n this.generateTrendData()\n }\n\n // 기간이 변경되면 차트 데이터 다시 가져오기\n if (changedProperties.has('startYearMonth') || changedProperties.has('endYearMonth')) {\n if (this.selectedRegion) {\n this.fetchRegionKpiStats()\n }\n }\n }\n\n async fetchRegionKpiStats() {\n if (!this.selectedRegion) return\n\n try {\n let regionKpiStats: any[]\n\n if (this.geoGroup) {\n // 시군구 모드: geo_group 코드로 조회\n const response = await client.query({\n query: gql`\n query GetSigunguKpiStats($geoGroup: String!, $startYearMonth: String, $endYearMonth: String) {\n kpiYValueStatsByGeoGroup(geoGroup: $geoGroup, startYearMonth: $startYearMonth, endYearMonth: $endYearMonth) {\n kpiName\n minVal\n q1Val\n medVal\n q3Val\n maxVal\n avgVal\n projectCount\n }\n }\n `,\n variables: {\n geoGroup: this.geoGroup,\n startYearMonth: this.startYearMonth,\n endYearMonth: this.endYearMonth\n }\n })\n regionKpiStats = response.data.kpiYValueStatsByGeoGroup || []\n } else {\n // 광역 모드: 광역시도 이름으로 조회\n const response = await client.query({\n query: gql`\n query GetRegionKpiStats($metroArea: String!, $startYearMonth: String, $endYearMonth: String) {\n kpiYValueStatsByMetroArea(metroArea: $metroArea, startYearMonth: $startYearMonth, endYearMonth: $endYearMonth) {\n kpiName\n minVal\n q1Val\n medVal\n q3Val\n maxVal\n avgVal\n projectCount\n }\n }\n `,\n variables: {\n metroArea: this.selectedRegion,\n startYearMonth: this.startYearMonth,\n endYearMonth: this.endYearMonth\n }\n })\n regionKpiStats = response.data.kpiYValueStatsByMetroArea || []\n }\n\n this.regionKpiStats = regionKpiStats\n this.generateChartData()\n } catch (error) {\n console.error('Failed to fetch region KPI stats:', error)\n this.regionKpiStats = []\n this.generateChartData()\n }\n }\n\n private generateChartData() {\n // KPI 이름을 카테고리로 매핑 및 축약\n const categoryMapping: Record<string, string> = {\n 'Y1. 일정성과': '일정',\n 'Y2. 비용성과': '비용',\n 'Y3. 품질성과': '품질',\n 'Y4. 안전성과': '안전',\n 'Y5. 환경성과': '환경',\n 'Y6. 생산성성과': '생산성'\n }\n\n // 레이더 차트와 박스플롯은 항상 전체 Y-level KPI 표시 (selectedCategory 무시)\n if (this.selectedChartType === 'radar') {\n // 레이더 차트용 데이터 생성 (전체평균 + 지역 비교)\n const data: Array<{ org: string; category: string; value: number }> = []\n const categories: string[] = []\n\n // 카테고리 수집\n this.regionKpiStats.forEach(stat => {\n const category = categoryMapping[stat.kpiName] || stat.kpiName\n if (!categories.includes(category)) {\n categories.push(category)\n }\n })\n\n // 전체평균 데이터 추가\n this.totalKpiStats.forEach(stat => {\n const category = categoryMapping[stat.kpiName] || stat.kpiName\n data.push({\n org: '전체평균',\n category,\n value: Math.round(stat.avgVal * 100)\n })\n })\n\n // 지역 데이터 추가\n this.regionKpiStats.forEach(stat => {\n const category = categoryMapping[stat.kpiName] || stat.kpiName\n data.push({\n org: this.selectedRegion || '지역',\n category,\n value: Math.round(stat.avgVal * 100)\n })\n })\n\n this.chartCategories = categories.length > 0 ? categories : ['일정', '비용', '품질', '안전', '환경', '생산성']\n this.chartData = data\n } else {\n // 박스플롯용 데이터 생성\n const data: Array<{\n org: string\n min: number\n max: number\n q1: number\n q3: number\n median: number\n mean: number\n value: number\n }> = []\n const categories: string[] = []\n\n this.regionKpiStats.forEach(stat => {\n const category = categoryMapping[stat.kpiName] || stat.kpiName\n if (!categories.includes(category)) {\n categories.push(category)\n }\n data.push({\n org: category,\n min: stat.minVal * 100,\n max: stat.maxVal * 100,\n q1: stat.q1Val * 100,\n q3: stat.q3Val * 100,\n median: stat.medVal * 100,\n mean: stat.avgVal * 100,\n value: stat.avgVal * 100\n })\n })\n\n this.chartCategories = categories.length > 0 ? categories : ['일정', '비용', '품질', '안전', '환경', '생산성']\n this.chartData = data\n }\n }\n\n // geo_group → 광역시도 매핑 (시군구 모드에서 추이 데이터 조회용)\n private static readonly GEO_TO_METRO: Record<string, string> = {\n '01':'서울특별시','02':'서울특별시','03':'서울특별시','04':'서울특별시',\n '05':'서울특별시','06':'서울특별시','07':'서울특별시','08':'서울특별시',\n '10':'경기도','11':'경기도','12':'경기도','13':'경기도','14':'경기도',\n '15':'경기도','16':'경기도','17':'경기도','18':'경기도',\n '21':'인천광역시','22':'인천광역시','23':'인천광역시',\n '24':'강원도','25':'강원도','26':'강원도','33':'강원도',\n '27':'충청북도','28':'충청북도','29':'충청북도',\n '30':'세종특별자치시','31':'충청남도','32':'충청남도',\n '34':'대전광역시','35':'대전광역시',\n '36':'경상북도','37':'경상북도','38':'경상북도','39':'경상북도','40':'경상북도',\n '41':'대구광역시','42':'대구광역시','43':'대구광역시',\n '44':'울산광역시','45':'울산광역시',\n '46':'부산광역시','47':'부산광역시','48':'부산광역시','49':'부산광역시',\n '50':'경상남도','51':'경상남도','52':'경상남도','53':'경상남도',\n '54':'전라북도','55':'전라북도','56':'전라북도',\n '57':'전라남도','58':'전라남도','59':'전라남도',\n '61':'광주광역시','62':'광주광역시','63':'제주특별자치도'\n }\n\n private generateTrendData() {\n if (!this.selectedRegion) return\n\n // 시군구 모드인 경우 소속 광역시도의 추이 데이터 사용\n const trendRegion = this.geoGroup\n ? KpiRegionPopup.GEO_TO_METRO[this.geoGroup] || this.selectedRegion\n : this.selectedRegion\n\n const regionTrendData = this.monthlyTrendData\n .filter(d => d.geoGroup === trendRegion)\n .sort((a, b) => a.yearMonth.localeCompare(b.yearMonth))\n\n this.trendData = regionTrendData.map(d => {\n const value = Math.round(d.avgVal * 100) // 20배 스케일\n return {\n date: d.yearMonth, // YYYY-MM 형식\n value: value,\n color: value > 60 ? '#4caf50' : value > 40 ? '#ff9800' : '#f44336'\n }\n })\n }\n\n private onClose() {\n this.dispatchEvent(\n new CustomEvent('popup-close', {\n bubbles: true,\n composed: true\n })\n )\n }\n\n private onChartTypeChange(type: string) {\n this.selectedChartType = type\n this.generateChartData()\n }\n\n render() {\n if (!this.selectedRegion) {\n return nothing\n }\n\n return html`\n <div class=\"popup-header\">\n <div class=\"popup-title\">${this.selectedRegion} KPI</div>\n <button class=\"popup-close\" @click=${this.onClose}>×</button>\n </div>\n <div class=\"popup-content\">\n <!-- 종합 성과 -->\n <div class=\"chart-section\">\n <div class=\"sub-title\">종합 성과</div>\n <div class=\"chart-toggle\">\n <button\n class=\"toggle-button ${this.selectedChartType === 'boxplot' ? 'active' : ''}\"\n @click=${() => this.onChartTypeChange('boxplot')}\n >\n 박스플롯\n </button>\n <button\n class=\"toggle-button ${this.selectedChartType === 'radar' ? 'active' : ''}\"\n @click=${() => this.onChartTypeChange('radar')}\n >\n 레이더차트\n </button>\n </div>\n <div class=\"chart-container\">\n ${this.selectedChartType === 'boxplot'\n ? html`\n <sv-kpi-boxplot-chart\n .data=${this.chartData}\n .groups=${this.chartCategories}\n .valueKey=${'value'}\n ></sv-kpi-boxplot-chart>\n `\n : html`\n <sv-kpi-radar-chart\n .data=${this.chartData}\n .categories=${this.chartCategories}\n .valueKey=${'value'}\n ></sv-kpi-radar-chart>\n `}\n </div>\n </div>\n\n <!-- 기간별 성과 추이 -->\n <div class=\"trend-section\">\n <div class=\"sub-title\">기간별 성과 추이</div>\n <div class=\"trend-chart-container\" style=\"height: 200px; margin-bottom: 16px;\">\n <sv-kpi-trend-chart\n .data=${this.trendData}\n .lineColor=${'#2196f3'}\n .strokeWidth=${2}\n .showPoints=${false}\n .pointRadius=${4}\n ></sv-kpi-trend-chart>\n </div>\n <div class=\"trend-table\">\n <table style=\"width: 100%; border-collapse: collapse; font-size: 0.85rem;\">\n <thead style=\"position: sticky; top: 0; background: white; z-index: 1;\">\n <tr style=\"border-bottom: 2px solid #f1f3f4; background-color: #f8f9fa;\">\n <th style=\"padding: 8px; text-align: left; font-weight: 600;\">날짜</th>\n <th style=\"padding: 8px; text-align: right; font-weight: 600;\">성과</th>\n <th style=\"padding: 8px; text-align: center; font-weight: 600;\">추이</th>\n </tr>\n </thead>\n <tbody>\n ${this.trendData.map(\n (item: any) => html`\n <tr style=\"border-bottom: 1px solid #f1f3f4;\">\n <td style=\"padding: 8px; font-size: 0.85rem;\">${item.date}</td>\n <td style=\"padding: 8px; text-align: right; font-size: 0.85rem; font-weight: 600;\">\n ${item.value.toFixed(1)}\n </td>\n <td style=\"padding: 8px; text-align: center; font-size: 0.85rem;\">\n <span style=\"color: ${item.color};\"> ${item.value > 60 ? '▲' : item.value > 40 ? '▲' : '▼'} </span>\n </td>\n </tr>\n `\n )}\n </tbody>\n </table>\n </div>\n </div>\n </div>\n `\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"kpi-region-popup.js","sourceRoot":"","sources":["../../../../client/pages/kpi-dashboard/components/kpi-region-popup.ts"],"names":[],"mappings":"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","sourcesContent":["import gql from 'graphql-tag'\nimport { LitElement, html, css, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { client } from '@operato/graphql'\nimport { ScrollbarStyles } from '@operato/styles'\n\nimport '../../../components/kpi-radar-chart.js'\nimport '../../../components/kpi-boxplot-chart.js'\nimport '../../../components/kpi-trend-chart.js'\n\n@customElement('kpi-region-popup')\nexport class KpiRegionPopup extends LitElement {\n static styles = [\n ScrollbarStyles,\n css`\n :host {\n display: block;\n position: absolute;\n top: 0;\n left: 402px;\n width: 400px;\n height: 100%;\n background: #fff;\n border-right: 1px solid #e0e0e0;\n z-index: 1000;\n overflow: hidden;\n display: flex;\n flex-direction: column;\n box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);\n }\n .popup-content {\n padding: 20px;\n overflow-y: auto;\n flex: 1;\n }\n .popup-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 20px;\n border-bottom: 1px solid #e0e0e0;\n background: #fff;\n height: 70px;\n box-sizing: border-box;\n }\n .popup-title {\n font-size: 1.2rem;\n font-weight: bold;\n color: #333;\n }\n .popup-close {\n width: 32px;\n height: 32px;\n border: none;\n background: #fff;\n border-radius: 50%;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 1.2rem;\n color: #666;\n transition: all 0.2s;\n }\n .popup-close:hover {\n background: #e9ecef;\n color: #333;\n }\n .sub-title {\n font-size: 1rem;\n font-weight: 600;\n margin-bottom: 16px;\n color: #495057;\n }\n .chart-section {\n background: #f8f9fa;\n border-radius: 8px;\n padding: 16px;\n margin-bottom: 20px;\n }\n .chart-toggle {\n display: flex;\n gap: 8px;\n margin-bottom: 16px;\n }\n .toggle-button {\n padding: 8px 16px;\n border: 1px solid #ced4da;\n background: #fff;\n border-radius: 6px;\n cursor: pointer;\n font-size: 0.9rem;\n transition: all 0.2s;\n }\n .toggle-button.active {\n background: #667eea;\n color: white;\n border-color: #667eea;\n }\n .chart-container {\n height: 300px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: white;\n border-radius: 6px;\n border: 1px solid #e9ecef;\n }\n .period-selector {\n display: flex;\n gap: 8px;\n margin-bottom: 16px;\n }\n .period-button {\n padding: 6px 12px;\n border: 1px solid #ced4da;\n background: #fff;\n border-radius: 4px;\n cursor: pointer;\n font-size: 0.85rem;\n transition: all 0.2s;\n }\n .period-button.active {\n background: #667eea;\n color: white;\n border-color: #667eea;\n }\n .date-range-selector {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 16px;\n }\n .date-select {\n padding: 6px 12px;\n border: 1px solid #ced4da;\n border-radius: 4px;\n background: white;\n font-size: 0.9rem;\n }\n `\n ]\n\n @property({ type: String }) selectedRegion: string | null = null\n @property({ type: String }) geoGroup: string | null = null // 시군구 geo_group 코드 (있으면 시군구 쿼리 사용)\n @property({ type: String }) selectedCategory = '전체 KPI'\n @property({ type: String }) selectedChartType = 'boxplot'\n @property({ type: String }) selectedPeriod = '월'\n @property({ type: String }) startYearMonth = '' // YYYY-MM 형식\n @property({ type: String }) endYearMonth = '' // YYYY-MM 형식\n @property({ type: Array }) monthlyTrendData: any[] = [] // 부모에서 전달받은 월별 추이 데이터\n @property({ type: Array }) totalKpiStats: any[] = [] // 전체 KPI 통계 (비교용)\n\n @state() private chartData: any[] = []\n @state() private chartCategories: string[] = []\n @state() private trendData: { date: string; value: number; color?: string; trendDir?: 'up' | 'down' | 'flat' }[] = []\n @state() private regionKpiStats: any[] = []\n\n updated(changedProperties: Map<string, any>) {\n if (changedProperties.has('selectedRegion') && this.selectedRegion) {\n this.fetchRegionKpiStats()\n this.generateTrendData()\n }\n\n // selectedCategory 또는 monthlyTrendData가 변경되면 추이 데이터 재생성\n if (changedProperties.has('selectedCategory') || changedProperties.has('monthlyTrendData')) {\n this.generateTrendData()\n }\n\n // 기간이 변경되면 차트 데이터 다시 가져오기\n if (changedProperties.has('startYearMonth') || changedProperties.has('endYearMonth')) {\n if (this.selectedRegion) {\n this.fetchRegionKpiStats()\n }\n // 기간이 바뀌면 추이도 그 기간 전체 축으로 다시 그림 (데이터 없는 월 포함)\n this.generateTrendData()\n }\n }\n\n async fetchRegionKpiStats() {\n if (!this.selectedRegion) return\n\n try {\n let regionKpiStats: any[]\n\n if (this.geoGroup) {\n // 시군구 모드: geo_group 코드로 조회\n const response = await client.query({\n query: gql`\n query GetSigunguKpiStats($geoGroup: String!, $startYearMonth: String, $endYearMonth: String) {\n kpiYValueStatsByGeoGroup(geoGroup: $geoGroup, startYearMonth: $startYearMonth, endYearMonth: $endYearMonth) {\n kpiName\n minVal\n q1Val\n medVal\n q3Val\n maxVal\n avgVal\n projectCount\n }\n }\n `,\n variables: {\n geoGroup: this.geoGroup,\n startYearMonth: this.startYearMonth,\n endYearMonth: this.endYearMonth\n }\n })\n regionKpiStats = response.data.kpiYValueStatsByGeoGroup || []\n } else {\n // 광역 모드: 광역시도 이름으로 조회\n const response = await client.query({\n query: gql`\n query GetRegionKpiStats($metroArea: String!, $startYearMonth: String, $endYearMonth: String) {\n kpiYValueStatsByMetroArea(metroArea: $metroArea, startYearMonth: $startYearMonth, endYearMonth: $endYearMonth) {\n kpiName\n minVal\n q1Val\n medVal\n q3Val\n maxVal\n avgVal\n projectCount\n }\n }\n `,\n variables: {\n metroArea: this.selectedRegion,\n startYearMonth: this.startYearMonth,\n endYearMonth: this.endYearMonth\n }\n })\n regionKpiStats = response.data.kpiYValueStatsByMetroArea || []\n }\n\n this.regionKpiStats = regionKpiStats\n this.generateChartData()\n } catch (error) {\n console.error('Failed to fetch region KPI stats:', error)\n this.regionKpiStats = []\n this.generateChartData()\n }\n }\n\n private generateChartData() {\n // kpiName 표기가 'Y1. 일정성과' / '일정성과(Y1)' 등으로 환경마다 달라 전체 문자열 매칭은 깨짐.\n // Y1~Y6 코드만 추출해 짧은 라벨로 매핑.\n const yLabelByCode: Record<string, string> = {\n Y1: '일정',\n Y2: '비용',\n Y3: '품질',\n Y4: '안전',\n Y5: '환경',\n Y6: '생산성'\n }\n const toCategory = (kpiName: string) => yLabelByCode[(kpiName || '').match(/Y[1-6]/)?.[0] ?? ''] || kpiName\n\n // 레이더 차트와 박스플롯은 항상 전체 Y-level KPI 표시 (selectedCategory 무시)\n if (this.selectedChartType === 'radar') {\n // 레이더 차트용 데이터 생성 — 지역 평균 단일 시리즈\n // (전체평균 기준선은 지역 평균과 차별성이 적어 제거 → 단일 시리즈라 범례도 자동 미표시)\n const data: Array<{ org: string; category: string; value: number }> = []\n const categories: string[] = []\n\n // 카테고리 수집\n this.regionKpiStats.forEach(stat => {\n const category = toCategory(stat.kpiName)\n if (!categories.includes(category)) {\n categories.push(category)\n }\n })\n\n // 지역 데이터 추가 (박스플롯과 동일 스케일·정밀도 — Math.round 제거해 수치 일치)\n this.regionKpiStats.forEach(stat => {\n const category = toCategory(stat.kpiName)\n data.push({\n org: this.selectedRegion || '지역',\n category,\n value: stat.avgVal * 100\n })\n })\n\n this.chartCategories = categories.length > 0 ? categories : ['일정', '비용', '품질', '안전', '환경', '생산성']\n this.chartData = data\n } else {\n // 박스플롯용 데이터 생성\n const data: Array<{\n org: string\n min: number\n max: number\n q1: number\n q3: number\n median: number\n mean: number\n value: number\n }> = []\n const categories: string[] = []\n\n this.regionKpiStats.forEach(stat => {\n const category = toCategory(stat.kpiName)\n if (!categories.includes(category)) {\n categories.push(category)\n }\n data.push({\n org: category,\n min: stat.minVal * 100,\n max: stat.maxVal * 100,\n q1: stat.q1Val * 100,\n q3: stat.q3Val * 100,\n median: stat.medVal * 100,\n mean: stat.avgVal * 100,\n value: stat.avgVal * 100\n })\n })\n\n this.chartCategories = categories.length > 0 ? categories : ['일정', '비용', '품질', '안전', '환경', '생산성']\n this.chartData = data\n }\n }\n\n // geo_group → 광역시도 매핑 (시군구 모드에서 추이 데이터 조회용)\n private static readonly GEO_TO_METRO: Record<string, string> = {\n '01': '서울특별시',\n '02': '서울특별시',\n '03': '서울특별시',\n '04': '서울특별시',\n '05': '서울특별시',\n '06': '서울특별시',\n '07': '서울특별시',\n '08': '서울특별시',\n '10': '경기도',\n '11': '경기도',\n '12': '경기도',\n '13': '경기도',\n '14': '경기도',\n '15': '경기도',\n '16': '경기도',\n '17': '경기도',\n '18': '경기도',\n '21': '인천광역시',\n '22': '인천광역시',\n '23': '인천광역시',\n '24': '강원도',\n '25': '강원도',\n '26': '강원도',\n '33': '강원도',\n '27': '충청북도',\n '28': '충청북도',\n '29': '충청북도',\n '30': '세종특별자치시',\n '31': '충청남도',\n '32': '충청남도',\n '34': '대전광역시',\n '35': '대전광역시',\n '36': '경상북도',\n '37': '경상북도',\n '38': '경상북도',\n '39': '경상북도',\n '40': '경상북도',\n '41': '대구광역시',\n '42': '대구광역시',\n '43': '대구광역시',\n '44': '울산광역시',\n '45': '울산광역시',\n '46': '부산광역시',\n '47': '부산광역시',\n '48': '부산광역시',\n '49': '부산광역시',\n '50': '경상남도',\n '51': '경상남도',\n '52': '경상남도',\n '53': '경상남도',\n '54': '전라북도',\n '55': '전라북도',\n '56': '전라북도',\n '57': '전라남도',\n '58': '전라남도',\n '59': '전라남도',\n '61': '광주광역시',\n '62': '광주광역시',\n '63': '제주특별자치도'\n }\n\n private generateTrendData() {\n if (!this.selectedRegion) return\n\n // 시군구 모드인 경우 소속 광역시도의 추이 데이터 사용\n const trendRegion = this.geoGroup ? KpiRegionPopup.GEO_TO_METRO[this.geoGroup] || this.selectedRegion : this.selectedRegion\n\n // 해당 지역의 월별 데이터를 YYYY-MM 기준 맵으로\n const byMonth = new Map<string, any>()\n this.monthlyTrendData.filter(d => d.geoGroup === trendRegion).forEach(d => byMonth.set(d.yearMonth, d))\n\n // 선택 기간(start~end) 전체 월을 축으로 사용 — 데이터가 없는 월도 레코드(값 0)로 채워\n // 전국 패널의 기간과 동일한 범위를 플로팅. (전체 기간 모드 등으로 start/end 가 비면 데이터 월만 사용)\n const months =\n this.startYearMonth && this.endYearMonth\n ? this.buildMonthRange(this.startYearMonth, this.endYearMonth)\n : [...byMonth.keys()].sort()\n\n let prev: number | null = null\n this.trendData = months.map(ym => {\n const d = byMonth.get(ym)\n const value = d ? Math.round(d.avgVal * 100) : 0 // 20배 스케일, 데이터 없으면 0\n // 직전 월 대비 증감 방향 — 추이 화살표/색상 기준 (값 임계치가 아니라 실제 증감)\n const trendDir: 'up' | 'down' | 'flat' = prev === null ? 'flat' : value > prev ? 'up' : value < prev ? 'down' : 'flat'\n prev = value\n return {\n date: ym, // YYYY-MM 형식\n value,\n color: value > 60 ? '#4caf50' : value > 40 ? '#ff9800' : '#f44336',\n trendDir\n }\n })\n }\n\n /** start~end(YYYY-MM) 사이 모든 월을 순서대로 반환. 잘못된 범위는 안전하게 종료. */\n private buildMonthRange(start: string, end: string): string[] {\n const [sy, sm] = start.split('-').map(Number)\n const [ey, em] = end.split('-').map(Number)\n if (!sy || !sm || !ey || !em) return []\n const out: string[] = []\n let y = sy\n let m = sm\n for (let i = 0; i < 240; i++) {\n out.push(`${y}-${String(m).padStart(2, '0')}`)\n if (y === ey && m === em) break\n if (y > ey || (y === ey && m > em)) break // start > end 방어\n m++\n if (m > 12) {\n m = 1\n y++\n }\n }\n return out\n }\n\n private onClose() {\n this.dispatchEvent(\n new CustomEvent('popup-close', {\n bubbles: true,\n composed: true\n })\n )\n }\n\n private onChartTypeChange(type: string) {\n this.selectedChartType = type\n this.generateChartData()\n }\n\n render() {\n if (!this.selectedRegion) {\n return nothing\n }\n\n return html`\n <div class=\"popup-header\">\n <div class=\"popup-title\">${this.selectedRegion} KPI</div>\n <button class=\"popup-close\" @click=${this.onClose}>×</button>\n </div>\n <div class=\"popup-content\">\n <!-- 종합 성과 -->\n <div class=\"chart-section\">\n <div class=\"sub-title\">종합 성과</div>\n <div class=\"chart-toggle\">\n <button\n class=\"toggle-button ${this.selectedChartType === 'boxplot' ? 'active' : ''}\"\n @click=${() => this.onChartTypeChange('boxplot')}\n >\n 박스플롯\n </button>\n <button\n class=\"toggle-button ${this.selectedChartType === 'radar' ? 'active' : ''}\"\n @click=${() => this.onChartTypeChange('radar')}\n >\n 레이더차트\n </button>\n </div>\n <div class=\"chart-container\">\n ${this.chartData.length === 0\n ? html`<div\n style=\"display:flex;align-items:center;justify-content:center;height:100%;color:#888;font-size:0.9rem;\"\n >\n 선택한 기간에 데이터가 없습니다.\n </div>`\n : this.selectedChartType === 'boxplot'\n ? html`\n <sv-kpi-boxplot-chart\n .data=${this.chartData}\n .groups=${this.chartCategories}\n .valueKey=${'value'}\n .valueLabel=${this.selectedRegion || '지역'}\n .showStatus=${false}\n ></sv-kpi-boxplot-chart>\n `\n : html`\n <sv-kpi-radar-chart\n .data=${this.chartData}\n .categories=${this.chartCategories}\n .valueKey=${'value'}\n ></sv-kpi-radar-chart>\n `}\n </div>\n </div>\n\n <!-- 기간별 성과 추이 -->\n <div class=\"trend-section\">\n <div class=\"sub-title\">기간별 성과 추이</div>\n <div class=\"trend-chart-container\" style=\"height: 200px; margin-bottom: 16px;\">\n <sv-kpi-trend-chart\n .data=${this.trendData}\n .lineColor=${'#2196f3'}\n .strokeWidth=${2}\n .showPoints=${false}\n .pointRadius=${4}\n ></sv-kpi-trend-chart>\n </div>\n <div class=\"trend-table\">\n <table style=\"width: 100%; border-collapse: collapse; font-size: 0.85rem;\">\n <thead style=\"position: sticky; top: 0; background: white; z-index: 1;\">\n <tr style=\"border-bottom: 2px solid #f1f3f4; background-color: #f8f9fa;\">\n <th style=\"padding: 8px; text-align: left; font-weight: 600;\">날짜</th>\n <th style=\"padding: 8px; text-align: right; font-weight: 600;\">성과</th>\n <th style=\"padding: 8px; text-align: center; font-weight: 600;\">추이</th>\n </tr>\n </thead>\n <tbody>\n ${[...this.trendData].reverse().map(\n (item: any) => html`\n <tr style=\"border-bottom: 1px solid #f1f3f4;\">\n <td style=\"padding: 8px; font-size: 0.85rem;\">${item.date}</td>\n <td style=\"padding: 8px; text-align: right; font-size: 0.85rem; font-weight: 600;\">\n ${item.value.toFixed(1)}\n </td>\n <td style=\"padding: 8px; text-align: center; font-size: 0.85rem;\">\n <span style=\"color: ${item.trendDir === 'up' ? '#e53935' : item.trendDir === 'down' ? '#2196f3' : '#999'};\">\n ${item.trendDir === 'up' ? '▲' : item.trendDir === 'down' ? '▼' : ''}\n </span>\n </td>\n </tr>\n `\n )}\n </tbody>\n </table>\n </div>\n </div>\n </div>\n `\n }\n}\n"]}
|