@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
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
import { __decorate, __metadata } from "tslib";
|
|
2
|
+
import { LitElement, html, css } from 'lit';
|
|
3
|
+
import { customElement, property, state } from 'lit/decorators.js';
|
|
4
|
+
import '../../../google-map/common-google-map.js';
|
|
5
|
+
let KpiMapPanel = class KpiMapPanel extends LitElement {
|
|
6
|
+
constructor() {
|
|
7
|
+
super(...arguments);
|
|
8
|
+
this.selectedCategory = '전체 KPI';
|
|
9
|
+
this.mapData = [];
|
|
10
|
+
this.map = null;
|
|
11
|
+
}
|
|
12
|
+
static { this.styles = css `
|
|
13
|
+
:host {
|
|
14
|
+
display: flex;
|
|
15
|
+
background: #f8f9fa;
|
|
16
|
+
overflow: hidden;
|
|
17
|
+
position: relative;
|
|
18
|
+
min-height: 500px;
|
|
19
|
+
}
|
|
20
|
+
.map-overlay {
|
|
21
|
+
position: absolute;
|
|
22
|
+
top: 16px;
|
|
23
|
+
left: 16px;
|
|
24
|
+
z-index: 10;
|
|
25
|
+
background: rgba(255, 255, 255, 0.95);
|
|
26
|
+
border-radius: 8px;
|
|
27
|
+
padding: 12px 16px;
|
|
28
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
29
|
+
backdrop-filter: blur(4px);
|
|
30
|
+
}
|
|
31
|
+
.category-buttons {
|
|
32
|
+
display: flex;
|
|
33
|
+
gap: 8px;
|
|
34
|
+
flex-wrap: wrap;
|
|
35
|
+
}
|
|
36
|
+
.category-button {
|
|
37
|
+
padding: 6px 12px;
|
|
38
|
+
border: 1px solid #ced4da;
|
|
39
|
+
background: #fff;
|
|
40
|
+
border-radius: 4px;
|
|
41
|
+
cursor: pointer;
|
|
42
|
+
font-size: 0.85rem;
|
|
43
|
+
transition: all 0.2s;
|
|
44
|
+
white-space: nowrap;
|
|
45
|
+
}
|
|
46
|
+
.category-button.active {
|
|
47
|
+
background: #667eea;
|
|
48
|
+
color: white;
|
|
49
|
+
border-color: #667eea;
|
|
50
|
+
}
|
|
51
|
+
.category-button:hover {
|
|
52
|
+
background: #f8f9fa;
|
|
53
|
+
}
|
|
54
|
+
.category-button.active:hover {
|
|
55
|
+
background: #5a6fd8;
|
|
56
|
+
}
|
|
57
|
+
.map-container {
|
|
58
|
+
flex: 1;
|
|
59
|
+
position: relative;
|
|
60
|
+
overflow: hidden;
|
|
61
|
+
}
|
|
62
|
+
.map-controls {
|
|
63
|
+
position: absolute;
|
|
64
|
+
top: 16px;
|
|
65
|
+
right: 16px;
|
|
66
|
+
display: flex;
|
|
67
|
+
flex-direction: column;
|
|
68
|
+
gap: 8px;
|
|
69
|
+
z-index: 10;
|
|
70
|
+
}
|
|
71
|
+
.map-control-button {
|
|
72
|
+
width: 40px;
|
|
73
|
+
height: 40px;
|
|
74
|
+
background: white;
|
|
75
|
+
border: 1px solid #ced4da;
|
|
76
|
+
border-radius: 6px;
|
|
77
|
+
cursor: pointer;
|
|
78
|
+
display: flex;
|
|
79
|
+
align-items: center;
|
|
80
|
+
justify-content: center;
|
|
81
|
+
font-size: 1.2rem;
|
|
82
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
83
|
+
}
|
|
84
|
+
.map-control-button:hover {
|
|
85
|
+
background: #f8f9fa;
|
|
86
|
+
}
|
|
87
|
+
.map-scale-direction {
|
|
88
|
+
position: absolute;
|
|
89
|
+
bottom: 16px;
|
|
90
|
+
right: 16px;
|
|
91
|
+
background: white;
|
|
92
|
+
padding: 8px 12px;
|
|
93
|
+
border-radius: 6px;
|
|
94
|
+
border: 1px solid #ced4da;
|
|
95
|
+
font-size: 0.8rem;
|
|
96
|
+
color: #666;
|
|
97
|
+
z-index: 10;
|
|
98
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
99
|
+
text-align: center;
|
|
100
|
+
}
|
|
101
|
+
.north-arrow {
|
|
102
|
+
font-size: 1rem;
|
|
103
|
+
margin-bottom: 4px;
|
|
104
|
+
}
|
|
105
|
+
.scale-info {
|
|
106
|
+
font-size: 0.7rem;
|
|
107
|
+
}
|
|
108
|
+
common-google-map {
|
|
109
|
+
width: 100%;
|
|
110
|
+
height: 100%;
|
|
111
|
+
}
|
|
112
|
+
`; }
|
|
113
|
+
// mapData를 지도 마커 형식으로 변환
|
|
114
|
+
get mapLocations() {
|
|
115
|
+
return (this.mapData?.map(item => ({
|
|
116
|
+
lat: item.lat,
|
|
117
|
+
lng: item.lng,
|
|
118
|
+
title: item.region,
|
|
119
|
+
region: item.region, // 지역명 추가
|
|
120
|
+
// 커스텀 마커 콘텐츠 생성
|
|
121
|
+
markerContent: `
|
|
122
|
+
<div style="
|
|
123
|
+
background: white;
|
|
124
|
+
border-radius: 8px;
|
|
125
|
+
padding: 8px 12px;
|
|
126
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
127
|
+
border: 1px solid #e9ecef;
|
|
128
|
+
min-width: 80px;
|
|
129
|
+
text-align: center;
|
|
130
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
131
|
+
cursor: pointer;
|
|
132
|
+
">
|
|
133
|
+
<div style="
|
|
134
|
+
font-size: 11px;
|
|
135
|
+
font-weight: 600;
|
|
136
|
+
color: #495057;
|
|
137
|
+
margin-bottom: 2px;
|
|
138
|
+
white-space: nowrap;
|
|
139
|
+
">${item.region}</div>
|
|
140
|
+
<div style="
|
|
141
|
+
font-size: 13px;
|
|
142
|
+
font-weight: 700;
|
|
143
|
+
color: #212529;
|
|
144
|
+
margin-bottom: 2px;
|
|
145
|
+
">${item.kpi}</div>
|
|
146
|
+
<div style="
|
|
147
|
+
font-size: 10px;
|
|
148
|
+
color: ${item.change > 0 ? '#dc3545' : '#198754'};
|
|
149
|
+
font-weight: 500;
|
|
150
|
+
">${item.change > 0 ? '▲' : '▼'} ${Math.abs(item.change)}%</div>
|
|
151
|
+
</div>
|
|
152
|
+
`,
|
|
153
|
+
content: `
|
|
154
|
+
<div style="padding: 12px; min-width: 200px;">
|
|
155
|
+
<h3 style="margin: 0 0 8px 0; font-size: 16px; color: #212529;">${item.region}</h3>
|
|
156
|
+
<div style="margin-bottom: 8px;">
|
|
157
|
+
<span style="font-size: 14px; color: #6c757d;">KPI: </span>
|
|
158
|
+
<span style="font-size: 16px; font-weight: 600; color: #212529;">${item.kpi}</span>
|
|
159
|
+
</div>
|
|
160
|
+
<div style="
|
|
161
|
+
font-size: 14px;
|
|
162
|
+
color: ${item.change > 0 ? '#dc3545' : '#198754'};
|
|
163
|
+
font-weight: 500;
|
|
164
|
+
">
|
|
165
|
+
${item.change > 0 ? '▲' : '▼'} ${Math.abs(item.change)}% 변화
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
`
|
|
169
|
+
})) || []);
|
|
170
|
+
}
|
|
171
|
+
onCategoryButtonClick(category) {
|
|
172
|
+
this.dispatchEvent(new CustomEvent('category-change', {
|
|
173
|
+
detail: { category },
|
|
174
|
+
bubbles: true,
|
|
175
|
+
composed: true
|
|
176
|
+
}));
|
|
177
|
+
}
|
|
178
|
+
onMapChange(event) {
|
|
179
|
+
this.map = event.detail;
|
|
180
|
+
}
|
|
181
|
+
onRegionClick(event) {
|
|
182
|
+
this.dispatchEvent(new CustomEvent('region-click', {
|
|
183
|
+
detail: event.detail,
|
|
184
|
+
bubbles: true,
|
|
185
|
+
composed: true
|
|
186
|
+
}));
|
|
187
|
+
}
|
|
188
|
+
zoomIn() {
|
|
189
|
+
if (this.map) {
|
|
190
|
+
this.map.setZoom(this.map.getZoom() + 1);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
zoomOut() {
|
|
194
|
+
if (this.map) {
|
|
195
|
+
this.map.setZoom(this.map.getZoom() - 1);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
resetView() {
|
|
199
|
+
if (this.map) {
|
|
200
|
+
this.map.setCenter({ lat: 36.5, lng: 127.5 });
|
|
201
|
+
this.map.setZoom(7);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
render() {
|
|
205
|
+
return html `
|
|
206
|
+
<div class="map-overlay">
|
|
207
|
+
<div class="category-buttons">
|
|
208
|
+
<button
|
|
209
|
+
class="category-button ${this.selectedCategory === '전체 KPI' ? 'active' : ''}"
|
|
210
|
+
@click=${() => this.onCategoryButtonClick('전체 KPI')}
|
|
211
|
+
>
|
|
212
|
+
전체 KPI
|
|
213
|
+
</button>
|
|
214
|
+
<button
|
|
215
|
+
class="category-button ${this.selectedCategory === '일정 성과' ? 'active' : ''}"
|
|
216
|
+
@click=${() => this.onCategoryButtonClick('일정 성과')}
|
|
217
|
+
>
|
|
218
|
+
일정 성과
|
|
219
|
+
</button>
|
|
220
|
+
<button
|
|
221
|
+
class="category-button ${this.selectedCategory === '비용 성과' ? 'active' : ''}"
|
|
222
|
+
@click=${() => this.onCategoryButtonClick('비용 성과')}
|
|
223
|
+
>
|
|
224
|
+
비용 성과
|
|
225
|
+
</button>
|
|
226
|
+
<button
|
|
227
|
+
class="category-button ${this.selectedCategory === '품질 성과' ? 'active' : ''}"
|
|
228
|
+
@click=${() => this.onCategoryButtonClick('품질 성과')}
|
|
229
|
+
>
|
|
230
|
+
품질 성과
|
|
231
|
+
</button>
|
|
232
|
+
<button
|
|
233
|
+
class="category-button ${this.selectedCategory === '안전 성과' ? 'active' : ''}"
|
|
234
|
+
@click=${() => this.onCategoryButtonClick('안전 성과')}
|
|
235
|
+
>
|
|
236
|
+
안전 성과
|
|
237
|
+
</button>
|
|
238
|
+
<button
|
|
239
|
+
class="category-button ${this.selectedCategory === '환경 성과' ? 'active' : ''}"
|
|
240
|
+
@click=${() => this.onCategoryButtonClick('환경 성과')}
|
|
241
|
+
>
|
|
242
|
+
환경 성과
|
|
243
|
+
</button>
|
|
244
|
+
</div>
|
|
245
|
+
</div>
|
|
246
|
+
|
|
247
|
+
<div class="map-container">
|
|
248
|
+
<!-- 지도 컨트롤 (오른쪽 상단) -->
|
|
249
|
+
<div class="map-controls">
|
|
250
|
+
<button class="map-control-button" title="확대" @click=${() => this.zoomIn()}>+</button>
|
|
251
|
+
<button class="map-control-button" title="축소" @click=${() => this.zoomOut()}>-</button>
|
|
252
|
+
<button class="map-control-button" title="뷰 초기화" @click=${() => this.resetView()}>⌖</button>
|
|
253
|
+
</div>
|
|
254
|
+
|
|
255
|
+
<!-- 스케일 및 방향 정보 (오른쪽 하단) -->
|
|
256
|
+
<div class="map-scale-direction">
|
|
257
|
+
<div class="north-arrow">↑ N</div>
|
|
258
|
+
<div class="scale-info">25km</div>
|
|
259
|
+
</div>
|
|
260
|
+
|
|
261
|
+
<!-- 공통 Google Maps 컴포넌트 사용 -->
|
|
262
|
+
<common-google-map
|
|
263
|
+
.center=${{ lat: 36.5, lng: 127.5 }}
|
|
264
|
+
.zoom=${7}
|
|
265
|
+
.locations=${this.mapLocations}
|
|
266
|
+
.clusterZoom=${10}
|
|
267
|
+
.controls=${{
|
|
268
|
+
zoomControl: false,
|
|
269
|
+
mapTypeControl: false,
|
|
270
|
+
scaleControl: false,
|
|
271
|
+
streetViewControl: false,
|
|
272
|
+
rotateControl: false,
|
|
273
|
+
fullscreenControl: false
|
|
274
|
+
}}
|
|
275
|
+
@map-change=${this.onMapChange}
|
|
276
|
+
@region-click=${this.onRegionClick}
|
|
277
|
+
></common-google-map>
|
|
278
|
+
</div>
|
|
279
|
+
`;
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
__decorate([
|
|
283
|
+
property({ type: String }),
|
|
284
|
+
__metadata("design:type", Object)
|
|
285
|
+
], KpiMapPanel.prototype, "selectedCategory", void 0);
|
|
286
|
+
__decorate([
|
|
287
|
+
property({ type: Array }),
|
|
288
|
+
__metadata("design:type", Array)
|
|
289
|
+
], KpiMapPanel.prototype, "mapData", void 0);
|
|
290
|
+
__decorate([
|
|
291
|
+
state(),
|
|
292
|
+
__metadata("design:type", Object)
|
|
293
|
+
], KpiMapPanel.prototype, "map", void 0);
|
|
294
|
+
KpiMapPanel = __decorate([
|
|
295
|
+
customElement('kpi-map-panel')
|
|
296
|
+
], KpiMapPanel);
|
|
297
|
+
export { KpiMapPanel };
|
|
298
|
+
//# sourceMappingURL=kpi-map-panel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kpi-map-panel.js","sourceRoot":"","sources":["../../../../client/pages/kpi-dashboard/components/kpi-map-panel.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, html, css, nothing } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport '../../../google-map/common-google-map.js'\n\ndeclare global {\n interface Window {\n google: any\n }\n}\n\n@customElement('kpi-map-panel')\nexport class KpiMapPanel extends LitElement {\n static styles = css`\n :host {\n display: flex;\n background: #f8f9fa;\n overflow: hidden;\n position: relative;\n min-height: 500px;\n }\n .map-overlay {\n position: absolute;\n top: 16px;\n left: 16px;\n z-index: 10;\n background: rgba(255, 255, 255, 0.95);\n border-radius: 8px;\n padding: 12px 16px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n backdrop-filter: blur(4px);\n }\n .category-buttons {\n display: flex;\n gap: 8px;\n flex-wrap: wrap;\n }\n .category-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 white-space: nowrap;\n }\n .category-button.active {\n background: #667eea;\n color: white;\n border-color: #667eea;\n }\n .category-button:hover {\n background: #f8f9fa;\n }\n .category-button.active:hover {\n background: #5a6fd8;\n }\n .map-container {\n flex: 1;\n position: relative;\n overflow: hidden;\n }\n .map-controls {\n position: absolute;\n top: 16px;\n right: 16px;\n display: flex;\n flex-direction: column;\n gap: 8px;\n z-index: 10;\n }\n .map-control-button {\n width: 40px;\n height: 40px;\n background: white;\n border: 1px solid #ced4da;\n border-radius: 6px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 1.2rem;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n }\n .map-control-button:hover {\n background: #f8f9fa;\n }\n .map-scale-direction {\n position: absolute;\n bottom: 16px;\n right: 16px;\n background: white;\n padding: 8px 12px;\n border-radius: 6px;\n border: 1px solid #ced4da;\n font-size: 0.8rem;\n color: #666;\n z-index: 10;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n text-align: center;\n }\n .north-arrow {\n font-size: 1rem;\n margin-bottom: 4px;\n }\n .scale-info {\n font-size: 0.7rem;\n }\n common-google-map {\n width: 100%;\n height: 100%;\n }\n `\n\n @property({ type: String }) selectedCategory = '전체 KPI'\n @property({ type: Array }) mapData: any[] = []\n\n @state() private map: any = null\n\n // mapData를 지도 마커 형식으로 변환\n get mapLocations() {\n return (\n this.mapData?.map(item => ({\n lat: item.lat,\n lng: item.lng,\n title: item.region,\n region: item.region, // 지역명 추가\n // 커스텀 마커 콘텐츠 생성\n markerContent: `\n <div style=\"\n background: white;\n border-radius: 8px;\n padding: 8px 12px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);\n border: 1px solid #e9ecef;\n min-width: 80px;\n text-align: center;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n cursor: pointer;\n \">\n <div style=\"\n font-size: 11px;\n font-weight: 600;\n color: #495057;\n margin-bottom: 2px;\n white-space: nowrap;\n \">${item.region}</div>\n <div style=\"\n font-size: 13px;\n font-weight: 700;\n color: #212529;\n margin-bottom: 2px;\n \">${item.kpi}</div>\n <div style=\"\n font-size: 10px;\n color: ${item.change > 0 ? '#dc3545' : '#198754'};\n font-weight: 500;\n \">${item.change > 0 ? '▲' : '▼'} ${Math.abs(item.change)}%</div>\n </div>\n `,\n content: `\n <div style=\"padding: 12px; min-width: 200px;\">\n <h3 style=\"margin: 0 0 8px 0; font-size: 16px; color: #212529;\">${item.region}</h3>\n <div style=\"margin-bottom: 8px;\">\n <span style=\"font-size: 14px; color: #6c757d;\">KPI: </span>\n <span style=\"font-size: 16px; font-weight: 600; color: #212529;\">${item.kpi}</span>\n </div>\n <div style=\"\n font-size: 14px;\n color: ${item.change > 0 ? '#dc3545' : '#198754'};\n font-weight: 500;\n \">\n ${item.change > 0 ? '▲' : '▼'} ${Math.abs(item.change)}% 변화\n </div>\n </div>\n `\n })) || []\n )\n }\n\n private onCategoryButtonClick(category: string) {\n this.dispatchEvent(\n new CustomEvent('category-change', {\n detail: { category },\n bubbles: true,\n composed: true\n })\n )\n }\n\n private onMapChange(event: CustomEvent) {\n this.map = event.detail\n }\n\n private onRegionClick(event: CustomEvent) {\n this.dispatchEvent(\n new CustomEvent('region-click', {\n detail: event.detail,\n bubbles: true,\n composed: true\n })\n )\n }\n\n private zoomIn() {\n if (this.map) {\n this.map.setZoom(this.map.getZoom() + 1)\n }\n }\n\n private zoomOut() {\n if (this.map) {\n this.map.setZoom(this.map.getZoom() - 1)\n }\n }\n\n private resetView() {\n if (this.map) {\n this.map.setCenter({ lat: 36.5, lng: 127.5 })\n this.map.setZoom(7)\n }\n }\n\n render() {\n return html`\n <div class=\"map-overlay\">\n <div class=\"category-buttons\">\n <button\n class=\"category-button ${this.selectedCategory === '전체 KPI' ? 'active' : ''}\"\n @click=${() => this.onCategoryButtonClick('전체 KPI')}\n >\n 전체 KPI\n </button>\n <button\n class=\"category-button ${this.selectedCategory === '일정 성과' ? 'active' : ''}\"\n @click=${() => this.onCategoryButtonClick('일정 성과')}\n >\n 일정 성과\n </button>\n <button\n class=\"category-button ${this.selectedCategory === '비용 성과' ? 'active' : ''}\"\n @click=${() => this.onCategoryButtonClick('비용 성과')}\n >\n 비용 성과\n </button>\n <button\n class=\"category-button ${this.selectedCategory === '품질 성과' ? 'active' : ''}\"\n @click=${() => this.onCategoryButtonClick('품질 성과')}\n >\n 품질 성과\n </button>\n <button\n class=\"category-button ${this.selectedCategory === '안전 성과' ? 'active' : ''}\"\n @click=${() => this.onCategoryButtonClick('안전 성과')}\n >\n 안전 성과\n </button>\n <button\n class=\"category-button ${this.selectedCategory === '환경 성과' ? 'active' : ''}\"\n @click=${() => this.onCategoryButtonClick('환경 성과')}\n >\n 환경 성과\n </button>\n </div>\n </div>\n\n <div class=\"map-container\">\n <!-- 지도 컨트롤 (오른쪽 상단) -->\n <div class=\"map-controls\">\n <button class=\"map-control-button\" title=\"확대\" @click=${() => this.zoomIn()}>+</button>\n <button class=\"map-control-button\" title=\"축소\" @click=${() => this.zoomOut()}>-</button>\n <button class=\"map-control-button\" title=\"뷰 초기화\" @click=${() => this.resetView()}>⌖</button>\n </div>\n\n <!-- 스케일 및 방향 정보 (오른쪽 하단) -->\n <div class=\"map-scale-direction\">\n <div class=\"north-arrow\">↑ N</div>\n <div class=\"scale-info\">25km</div>\n </div>\n\n <!-- 공통 Google Maps 컴포넌트 사용 -->\n <common-google-map\n .center=${{ lat: 36.5, lng: 127.5 }}\n .zoom=${7}\n .locations=${this.mapLocations}\n .clusterZoom=${10}\n .controls=${{\n zoomControl: false,\n mapTypeControl: false,\n scaleControl: false,\n streetViewControl: false,\n rotateControl: false,\n fullscreenControl: false\n }}\n @map-change=${this.onMapChange}\n @region-click=${this.onRegionClick}\n ></common-google-map>\n </div>\n `\n }\n}\n"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { LitElement, nothing } from 'lit';
|
|
2
|
+
import '../../../charts/kpi-radar-chart.js';
|
|
3
|
+
import '../../../charts/kpi-boxplot-chart.js';
|
|
4
|
+
import '../../../charts/kpi-trend-chart.js';
|
|
5
|
+
export declare class KpiRegionPopup extends LitElement {
|
|
6
|
+
static styles: import("lit").CSSResult;
|
|
7
|
+
selectedRegion: string | null;
|
|
8
|
+
selectedChartType: string;
|
|
9
|
+
selectedPeriod: string;
|
|
10
|
+
startDate: string;
|
|
11
|
+
endDate: string;
|
|
12
|
+
private chartData;
|
|
13
|
+
private chartCategories;
|
|
14
|
+
private trendData;
|
|
15
|
+
connectedCallback(): void;
|
|
16
|
+
private generateChartData;
|
|
17
|
+
private generateTrendData;
|
|
18
|
+
private onClose;
|
|
19
|
+
private onChartTypeChange;
|
|
20
|
+
private onPeriodChange;
|
|
21
|
+
private onDateRangeChange;
|
|
22
|
+
render(): import("lit-html").TemplateResult<1> | typeof nothing;
|
|
23
|
+
}
|