@dssp/dkpi 1.0.0-alpha.65 → 1.0.0-alpha.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/_index.html +0 -5
  2. package/dist-client/components/kpi-2d-lookup-chart.d.ts +63 -0
  3. package/dist-client/components/kpi-2d-lookup-chart.js +470 -0
  4. package/dist-client/components/kpi-2d-lookup-chart.js.map +1 -0
  5. package/dist-client/google-map/common-google-map.js +10 -8
  6. package/dist-client/google-map/common-google-map.js.map +1 -1
  7. package/dist-client/pages/kpi-admin/dssp-kpi-list-page.d.ts +22 -0
  8. package/dist-client/pages/kpi-admin/dssp-kpi-list-page.js +57 -0
  9. package/dist-client/pages/kpi-admin/dssp-kpi-list-page.js.map +1 -0
  10. package/dist-client/pages/kpi-admin/kpi-grade-2d-editor.d.ts +20 -0
  11. package/dist-client/pages/kpi-admin/kpi-grade-2d-editor.js +445 -0
  12. package/dist-client/pages/kpi-admin/kpi-grade-2d-editor.js.map +1 -0
  13. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.d.ts +6 -5
  14. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js +47 -68
  15. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js.map +1 -1
  16. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.d.ts +3 -2
  17. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js +79 -122
  18. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js.map +1 -1
  19. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.d.ts +3 -2
  20. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js +71 -107
  21. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js.map +1 -1
  22. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.d.ts +4 -0
  23. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js +246 -28
  24. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js.map +1 -1
  25. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.d.ts +4 -0
  26. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js +22 -207
  27. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js.map +1 -1
  28. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.d.ts +2 -0
  29. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js +84 -25
  30. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js.map +1 -1
  31. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.d.ts +16 -0
  32. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js +261 -31
  33. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js.map +1 -1
  34. package/dist-client/pages/kpi-dashboard/kpi-dashboard.d.ts +4 -0
  35. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js +66 -4
  36. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js.map +1 -1
  37. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.d.ts +1 -2
  38. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js +1 -2
  39. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js.map +1 -1
  40. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.d.ts +1 -2
  41. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js +1 -2
  42. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js.map +1 -1
  43. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.d.ts +1 -2
  44. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js +1 -2
  45. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js.map +1 -1
  46. package/dist-client/pages/kpi-value/kpi-value-list-page.d.ts +1 -2
  47. package/dist-client/pages/kpi-value/kpi-value-list-page.js +1 -2
  48. package/dist-client/pages/kpi-value/kpi-value-list-page.js.map +1 -1
  49. package/dist-client/pages/sv-project-detail.d.ts +1 -0
  50. package/dist-client/pages/sv-project-detail.js +26 -13
  51. package/dist-client/pages/sv-project-detail.js.map +1 -1
  52. package/dist-client/pages/sv-project-list.js +6 -6
  53. package/dist-client/pages/sv-project-list.js.map +1 -1
  54. package/dist-client/route.d.ts +1 -1
  55. package/dist-client/route.js +4 -0
  56. package/dist-client/route.js.map +1 -1
  57. package/dist-client/tsconfig.tsbuildinfo +1 -1
  58. package/dist-client/viewparts/menu-tools.d.ts +1 -2
  59. package/dist-client/viewparts/menu-tools.js +1 -2
  60. package/dist-client/viewparts/menu-tools.js.map +1 -1
  61. package/dist-server/scripts/calculate-kpi-scores.js +65 -3
  62. package/dist-server/scripts/calculate-kpi-scores.js.map +1 -1
  63. package/dist-server/scripts/load-grade-data-migration.d.ts +4 -0
  64. package/dist-server/scripts/load-grade-data-migration.js +95 -10
  65. package/dist-server/scripts/load-grade-data-migration.js.map +1 -1
  66. package/dist-server/scripts/propagate-parent-kpi-values.js +58 -4
  67. package/dist-server/scripts/propagate-parent-kpi-values.js.map +1 -1
  68. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.d.ts +6 -0
  69. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js +57 -7
  70. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js.map +1 -1
  71. package/dist-server/service/kpi-stat/kpi-stat-query.d.ts +8 -5
  72. package/dist-server/service/kpi-stat/kpi-stat-query.js +228 -11
  73. package/dist-server/service/kpi-stat/kpi-stat-query.js.map +1 -1
  74. package/dist-server/service/kpi-stat/kpi-stat-types.d.ts +13 -0
  75. package/dist-server/service/kpi-stat/kpi-stat-types.js +51 -1
  76. package/dist-server/service/kpi-stat/kpi-stat-types.js.map +1 -1
  77. package/dist-server/tsconfig.tsbuildinfo +1 -1
  78. package/package.json +54 -54
  79. package/schema.graphql +95 -58
  80. package/things-factory.config.js +3 -1
  81. package/views/auth-page.html +0 -1
  82. package/views/public/home.html +0 -1
@@ -1,11 +1,14 @@
1
+ var KpiMapPanel_1;
1
2
  import { __decorate, __metadata } from "tslib";
2
3
  import { LitElement, html, css } from 'lit';
3
4
  import { customElement, property, state } from 'lit/decorators.js';
4
5
  import '../../../google-map/common-google-map.js';
5
- let KpiMapPanel = class KpiMapPanel extends LitElement {
6
+ let KpiMapPanel = KpiMapPanel_1 = class KpiMapPanel extends LitElement {
6
7
  constructor() {
7
8
  super(...arguments);
8
9
  this.selectedCategory = '전체 KPI';
10
+ this.sectorType = '';
11
+ this.buildingUsage = '';
9
12
  this.mapData = [];
10
13
  this.map = null;
11
14
  this.isAllPeriod = false; // 전체 기간 체크박스
@@ -130,6 +133,12 @@ let KpiMapPanel = class KpiMapPanel extends LitElement {
130
133
  composed: true
131
134
  }));
132
135
  }
136
+ focusOnLocation(lat, lng, zoom) {
137
+ if (this.map) {
138
+ this.map.setCenter({ lat, lng });
139
+ this.map.setZoom(zoom);
140
+ }
141
+ }
133
142
  zoomIn() {
134
143
  if (this.map) {
135
144
  this.map.setZoom(this.map.getZoom() + 1);
@@ -160,138 +169,6 @@ let KpiMapPanel = class KpiMapPanel extends LitElement {
160
169
  }
161
170
  render() {
162
171
  return html `
163
- <div class="map-overlay">
164
- <div class="category-buttons">
165
- <button
166
- class="category-button ${this.selectedCategory === '전체 KPI' ? 'active' : ''}"
167
- @click=${() => this.onCategoryButtonClick('전체 KPI')}
168
- >
169
- 전체 KPI
170
- </button>
171
- <button
172
- class="category-button ${this.selectedCategory === '일정 성과' ? 'active' : ''}"
173
- @click=${() => this.onCategoryButtonClick('일정 성과')}
174
- >
175
- 일정 성과
176
- </button>
177
- <button
178
- class="category-button ${this.selectedCategory === '비용 성과' ? 'active' : ''}"
179
- @click=${() => this.onCategoryButtonClick('비용 성과')}
180
- >
181
- 비용 성과
182
- </button>
183
- <button
184
- class="category-button ${this.selectedCategory === '품질 성과' ? 'active' : ''}"
185
- @click=${() => this.onCategoryButtonClick('품질 성과')}
186
- >
187
- 품질 성과
188
- </button>
189
- <button
190
- class="category-button ${this.selectedCategory === '안전 성과' ? 'active' : ''}"
191
- @click=${() => this.onCategoryButtonClick('안전 성과')}
192
- >
193
- 안전 성과
194
- </button>
195
- <button
196
- class="category-button ${this.selectedCategory === '환경 성과' ? 'active' : ''}"
197
- @click=${() => this.onCategoryButtonClick('환경 성과')}
198
- >
199
- 환경 성과
200
- </button>
201
- <button
202
- class="category-button ${this.selectedCategory === '생산성 성과' ? 'active' : ''}"
203
- @click=${() => this.onCategoryButtonClick('생산성 성과')}
204
- >
205
- 생산성 성과
206
- </button>
207
- </div>
208
-
209
- <!-- 기간 선택 폼 -->
210
- <div class="period-form">
211
- <div class="period-row">
212
- <span class="period-label">기간:</span>
213
- <select
214
- class="period-select"
215
- .value=${this.startYear}
216
- ?disabled=${this.isAllPeriod}
217
- @change=${(e) => {
218
- this.startYear = e.target.value;
219
- this.onPeriodChange();
220
- }}
221
- >
222
- <option value="2025">2025년</option>
223
- <option value="2024">2024년</option>
224
- <option value="2023">2023년</option>
225
- <option value="2022">2022년</option>
226
- </select>
227
- <select
228
- class="period-select"
229
- .value=${this.startMonth}
230
- ?disabled=${this.isAllPeriod}
231
- @change=${(e) => {
232
- this.startMonth = e.target.value;
233
- this.onPeriodChange();
234
- }}
235
- >
236
- <option value="1">1월</option>
237
- <option value="2">2월</option>
238
- <option value="3">3월</option>
239
- <option value="4">4월</option>
240
- <option value="5">5월</option>
241
- <option value="6">6월</option>
242
- <option value="7">7월</option>
243
- <option value="8">8월</option>
244
- <option value="9">9월</option>
245
- <option value="10">10월</option>
246
- <option value="11">11월</option>
247
- <option value="12">12월</option>
248
- </select>
249
- <span class="period-separator">~</span>
250
- <select
251
- class="period-select"
252
- .value=${this.endYear}
253
- ?disabled=${this.isAllPeriod}
254
- @change=${(e) => {
255
- this.endYear = e.target.value;
256
- this.onPeriodChange();
257
- }}
258
- >
259
- <option value="2025">2025년</option>
260
- <option value="2024">2024년</option>
261
- <option value="2023">2023년</option>
262
- <option value="2022">2022년</option>
263
- </select>
264
- <select
265
- class="period-select"
266
- .value=${this.endMonth}
267
- ?disabled=${this.isAllPeriod}
268
- @change=${(e) => {
269
- this.endMonth = e.target.value;
270
- this.onPeriodChange();
271
- }}
272
- >
273
- <option value="1">1월</option>
274
- <option value="2">2월</option>
275
- <option value="3">3월</option>
276
- <option value="4">4월</option>
277
- <option value="5">5월</option>
278
- <option value="6">6월</option>
279
- <option value="7">7월</option>
280
- <option value="8">8월</option>
281
- <option value="9">9월</option>
282
- <option value="10">10월</option>
283
- <option value="11">11월</option>
284
- <option value="12">12월</option>
285
- </select>
286
-
287
- <label style="display: flex; align-items: center; gap: 6px; cursor: pointer;">
288
- <input type="checkbox" .checked=${this.isAllPeriod} @change=${this.onAllPeriodChange} style="cursor: pointer;" />
289
- <span class="period-label" style="min-width: auto;">전체 기간</span>
290
- </label>
291
- </div>
292
- </div>
293
- </div>
294
-
295
172
  <div class="map-container">
296
173
  <!-- 지도 컨트롤 (오른쪽 상단) -->
297
174
  <div class="map-controls">
@@ -308,10 +185,10 @@ let KpiMapPanel = class KpiMapPanel extends LitElement {
308
185
 
309
186
  <!-- 공통 Google Maps 컴포넌트 사용 -->
310
187
  <common-google-map
311
- .center=${{ lat: 36.0, lng: 127.8 }}
188
+ .center=${KpiMapPanel_1.DEFAULT_CENTER}
312
189
  .zoom=${7.2}
313
190
  .locations=${this.mapLocations}
314
- .clusterZoom=${10}
191
+ .clusterZoom=${13}
315
192
  .controls=${{
316
193
  zoomControl: false,
317
194
  mapTypeControl: false,
@@ -335,77 +212,6 @@ KpiMapPanel.styles = css `
335
212
  position: relative;
336
213
  min-height: 500px;
337
214
  }
338
- .map-overlay {
339
- position: absolute;
340
- top: 16px;
341
- left: 16px;
342
- z-index: 10;
343
- background: rgba(255, 255, 255, 0.95);
344
- border-radius: 8px;
345
- padding: 12px 16px;
346
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
347
- backdrop-filter: blur(4px);
348
- }
349
- .category-buttons {
350
- display: flex;
351
- gap: 8px;
352
- flex-wrap: wrap;
353
- }
354
- .category-button {
355
- padding: 6px 12px;
356
- border: 1px solid #ced4da;
357
- background: #fff;
358
- border-radius: 4px;
359
- cursor: pointer;
360
- font-size: 0.85rem;
361
- transition: all 0.2s;
362
- white-space: nowrap;
363
- }
364
- .category-button.active {
365
- background: #667eea;
366
- color: white;
367
- border-color: #667eea;
368
- }
369
- .category-button:hover {
370
- background: #f8f9fa;
371
- }
372
- .category-button.active:hover {
373
- background: #5a6fd8;
374
- }
375
- .period-form {
376
- margin-top: 12px;
377
- padding-top: 12px;
378
- border-top: 1px solid #e9ecef;
379
- }
380
- .period-row {
381
- display: flex;
382
- align-items: center;
383
- gap: 8px;
384
- margin-bottom: 8px;
385
- }
386
- .period-label {
387
- font-size: 0.85rem;
388
- font-weight: 600;
389
- color: #495057;
390
- min-width: 40px;
391
- }
392
- .period-select {
393
- padding: 4px 8px;
394
- border: 1px solid #ced4da;
395
- border-radius: 4px;
396
- background: white;
397
- font-size: 0.8rem;
398
- cursor: pointer;
399
- }
400
- .period-select:focus {
401
- outline: none;
402
- border-color: #667eea;
403
- }
404
- .period-separator {
405
- font-size: 0.85rem;
406
- color: #6c757d;
407
- margin: 0 4px;
408
- }
409
215
  .map-container {
410
216
  flex: 1;
411
217
  position: relative;
@@ -462,10 +268,19 @@ KpiMapPanel.styles = css `
462
268
  height: 100%;
463
269
  }
464
270
  `;
271
+ KpiMapPanel.DEFAULT_CENTER = { lat: 36.0, lng: 127.8 };
465
272
  __decorate([
466
273
  property({ type: String }),
467
274
  __metadata("design:type", Object)
468
275
  ], KpiMapPanel.prototype, "selectedCategory", void 0);
276
+ __decorate([
277
+ property({ type: String }),
278
+ __metadata("design:type", Object)
279
+ ], KpiMapPanel.prototype, "sectorType", void 0);
280
+ __decorate([
281
+ property({ type: String }),
282
+ __metadata("design:type", Object)
283
+ ], KpiMapPanel.prototype, "buildingUsage", void 0);
469
284
  __decorate([
470
285
  property({ type: Array }),
471
286
  __metadata("design:type", Array)
@@ -494,7 +309,7 @@ __decorate([
494
309
  state(),
495
310
  __metadata("design:type", Object)
496
311
  ], KpiMapPanel.prototype, "endMonth", void 0);
497
- KpiMapPanel = __decorate([
312
+ KpiMapPanel = KpiMapPanel_1 = __decorate([
498
313
  customElement('kpi-map-panel')
499
314
  ], KpiMapPanel);
500
315
  export { KpiMapPanel };
@@ -1 +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 .period-form {\n margin-top: 12px;\n padding-top: 12px;\n border-top: 1px solid #e9ecef;\n }\n .period-row {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 8px;\n }\n .period-label {\n font-size: 0.85rem;\n font-weight: 600;\n color: #495057;\n min-width: 40px;\n }\n .period-select {\n padding: 4px 8px;\n border: 1px solid #ced4da;\n border-radius: 4px;\n background: white;\n font-size: 0.8rem;\n cursor: pointer;\n }\n .period-select:focus {\n outline: none;\n border-color: #667eea;\n }\n .period-separator {\n font-size: 0.85rem;\n color: #6c757d;\n margin: 0 4px;\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 @state() private isAllPeriod = false // 전체 기간 체크박스\n @state() private startYear = ''\n @state() private startMonth = ''\n @state() private endYear = ''\n @state() private endMonth = ''\n\n connectedCallback() {\n super.connectedCallback()\n this.initializeDefaultPeriod()\n }\n\n private initializeDefaultPeriod() {\n const now = new Date()\n const currentYear = now.getFullYear()\n const currentMonth = now.getMonth() + 1 // 0-based이므로 +1\n\n // 12개월 전 계산\n const startDate = new Date(now)\n startDate.setMonth(startDate.getMonth() - 11) // 현재 월 포함 12개월\n const startYear = startDate.getFullYear()\n const startMonth = startDate.getMonth() + 1\n\n this.startYear = startYear.toString()\n this.startMonth = startMonth.toString()\n this.endYear = currentYear.toString()\n this.endMonth = currentMonth.toString()\n }\n\n private onAllPeriodChange(e: Event) {\n this.isAllPeriod = (e.target as HTMLInputElement).checked\n this.onPeriodChange()\n }\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 this.fitBoundsToSouthKorea()\n }\n\n private fitBoundsToSouthKorea() {\n if (!this.map || !window.google) return\n\n // 남한의 실제 행정구역 경계 설정 (울릉도/독도 제외)\n const bounds = new window.google.maps.LatLngBounds()\n\n // 최북단: 강원도 고성 (DMZ 남쪽)\n bounds.extend(new window.google.maps.LatLng(38.3, 128.3))\n // 최남단: 제주도 남쪽\n bounds.extend(new window.google.maps.LatLng(33.1, 126.3))\n // 최서단: 전남 서쪽\n bounds.extend(new window.google.maps.LatLng(34.5, 125.1))\n // 최동단: 강원도 동해안\n bounds.extend(new window.google.maps.LatLng(37.5, 129.4))\n\n // 패딩을 추가하여 여유 공간 확보\n this.map.fitBounds(bounds, { top: 30, bottom: 30, left: 30, right: 30 })\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.fitBoundsToSouthKorea()\n }\n }\n\n private onPeriodChange() {\n this.dispatchEvent(\n new CustomEvent('period-change', {\n detail: {\n startYear: this.isAllPeriod ? null : this.startYear,\n startMonth: this.isAllPeriod ? null : this.startMonth,\n endYear: this.isAllPeriod ? null : this.endYear,\n endMonth: this.isAllPeriod ? null : this.endMonth,\n isAllPeriod: this.isAllPeriod\n },\n bubbles: true,\n composed: true\n })\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 <button\n class=\"category-button ${this.selectedCategory === '생산성 성과' ? 'active' : ''}\"\n @click=${() => this.onCategoryButtonClick('생산성 성과')}\n >\n 생산성 성과\n </button>\n </div>\n\n <!-- 기간 선택 폼 -->\n <div class=\"period-form\">\n <div class=\"period-row\">\n <span class=\"period-label\">기간:</span>\n <select\n class=\"period-select\"\n .value=${this.startYear}\n ?disabled=${this.isAllPeriod}\n @change=${(e: Event) => {\n this.startYear = (e.target as HTMLSelectElement).value\n this.onPeriodChange()\n }}\n >\n <option value=\"2025\">2025년</option>\n <option value=\"2024\">2024년</option>\n <option value=\"2023\">2023년</option>\n <option value=\"2022\">2022년</option>\n </select>\n <select\n class=\"period-select\"\n .value=${this.startMonth}\n ?disabled=${this.isAllPeriod}\n @change=${(e: Event) => {\n this.startMonth = (e.target as HTMLSelectElement).value\n this.onPeriodChange()\n }}\n >\n <option value=\"1\">1월</option>\n <option value=\"2\">2월</option>\n <option value=\"3\">3월</option>\n <option value=\"4\">4월</option>\n <option value=\"5\">5월</option>\n <option value=\"6\">6월</option>\n <option value=\"7\">7월</option>\n <option value=\"8\">8월</option>\n <option value=\"9\">9월</option>\n <option value=\"10\">10월</option>\n <option value=\"11\">11월</option>\n <option value=\"12\">12월</option>\n </select>\n <span class=\"period-separator\">~</span>\n <select\n class=\"period-select\"\n .value=${this.endYear}\n ?disabled=${this.isAllPeriod}\n @change=${(e: Event) => {\n this.endYear = (e.target as HTMLSelectElement).value\n this.onPeriodChange()\n }}\n >\n <option value=\"2025\">2025년</option>\n <option value=\"2024\">2024년</option>\n <option value=\"2023\">2023년</option>\n <option value=\"2022\">2022년</option>\n </select>\n <select\n class=\"period-select\"\n .value=${this.endMonth}\n ?disabled=${this.isAllPeriod}\n @change=${(e: Event) => {\n this.endMonth = (e.target as HTMLSelectElement).value\n this.onPeriodChange()\n }}\n >\n <option value=\"1\">1월</option>\n <option value=\"2\">2월</option>\n <option value=\"3\">3월</option>\n <option value=\"4\">4월</option>\n <option value=\"5\">5월</option>\n <option value=\"6\">6월</option>\n <option value=\"7\">7월</option>\n <option value=\"8\">8월</option>\n <option value=\"9\">9월</option>\n <option value=\"10\">10월</option>\n <option value=\"11\">11월</option>\n <option value=\"12\">12월</option>\n </select>\n\n <label style=\"display: flex; align-items: center; gap: 6px; cursor: pointer;\">\n <input type=\"checkbox\" .checked=${this.isAllPeriod} @change=${this.onAllPeriodChange} style=\"cursor: pointer;\" />\n <span class=\"period-label\" style=\"min-width: auto;\">전체 기간</span>\n </label>\n </div>\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.0, lng: 127.8 }}\n .zoom=${7.2}\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"]}
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-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: String }) sectorType = ''\n @property({ type: String }) buildingUsage = ''\n @property({ type: Array }) mapData: any[] = []\n\n private static readonly DEFAULT_CENTER = { lat: 36.0, lng: 127.8 }\n\n @state() private map: any = null\n @state() private isAllPeriod = false // 전체 기간 체크박스\n @state() private startYear = ''\n @state() private startMonth = ''\n @state() private endYear = ''\n @state() private endMonth = ''\n\n connectedCallback() {\n super.connectedCallback()\n this.initializeDefaultPeriod()\n }\n\n private initializeDefaultPeriod() {\n const now = new Date()\n const currentYear = now.getFullYear()\n const currentMonth = now.getMonth() + 1 // 0-based이므로 +1\n\n // 12개월 전 계산\n const startDate = new Date(now)\n startDate.setMonth(startDate.getMonth() - 11) // 현재 월 포함 12개월\n const startYear = startDate.getFullYear()\n const startMonth = startDate.getMonth() + 1\n\n this.startYear = startYear.toString()\n this.startMonth = startMonth.toString()\n this.endYear = currentYear.toString()\n this.endMonth = currentMonth.toString()\n }\n\n private onAllPeriodChange(e: Event) {\n this.isAllPeriod = (e.target as HTMLInputElement).checked\n this.onPeriodChange()\n }\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 this.fitBoundsToSouthKorea()\n }\n\n private fitBoundsToSouthKorea() {\n if (!this.map || !window.google) return\n\n // 남한의 실제 행정구역 경계 설정 (울릉도/독도 제외)\n const bounds = new window.google.maps.LatLngBounds()\n\n // 최북단: 강원도 고성 (DMZ 남쪽)\n bounds.extend(new window.google.maps.LatLng(38.3, 128.3))\n // 최남단: 제주도 남쪽\n bounds.extend(new window.google.maps.LatLng(33.1, 126.3))\n // 최서단: 전남 서쪽\n bounds.extend(new window.google.maps.LatLng(34.5, 125.1))\n // 최동단: 강원도 동해안\n bounds.extend(new window.google.maps.LatLng(37.5, 129.4))\n\n // 패딩을 추가하여 여유 공간 확보\n this.map.fitBounds(bounds, { top: 30, bottom: 30, left: 30, right: 30 })\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 focusOnLocation(lat: number, lng: number, zoom: number) {\n if (this.map) {\n this.map.setCenter({ lat, lng })\n this.map.setZoom(zoom)\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.fitBoundsToSouthKorea()\n }\n }\n\n private onPeriodChange() {\n this.dispatchEvent(\n new CustomEvent('period-change', {\n detail: {\n startYear: this.isAllPeriod ? null : this.startYear,\n startMonth: this.isAllPeriod ? null : this.startMonth,\n endYear: this.isAllPeriod ? null : this.endYear,\n endMonth: this.isAllPeriod ? null : this.endMonth,\n isAllPeriod: this.isAllPeriod\n },\n bubbles: true,\n composed: true\n })\n )\n }\n\n render() {\n return html`\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=${KpiMapPanel.DEFAULT_CENTER}\n .zoom=${7.2}\n .locations=${this.mapLocations}\n .clusterZoom=${13}\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"]}
@@ -5,6 +5,7 @@ import '../../../components/kpi-trend-chart.js';
5
5
  export declare class KpiRegionPopup extends LitElement {
6
6
  static styles: import("lit").CSSResult[];
7
7
  selectedRegion: string | null;
8
+ geoGroup: string | null;
8
9
  selectedCategory: string;
9
10
  selectedChartType: string;
10
11
  selectedPeriod: string;
@@ -19,6 +20,7 @@ export declare class KpiRegionPopup extends LitElement {
19
20
  updated(changedProperties: Map<string, any>): void;
20
21
  fetchRegionKpiStats(): Promise<void>;
21
22
  private generateChartData;
23
+ private static readonly GEO_TO_METRO;
22
24
  private generateTrendData;
23
25
  private onClose;
24
26
  private onChartTypeChange;
@@ -1,3 +1,4 @@
1
+ var KpiRegionPopup_1;
1
2
  import { __decorate, __metadata } from "tslib";
2
3
  import gql from 'graphql-tag';
3
4
  import { LitElement, html, css, nothing } from 'lit';
@@ -7,10 +8,11 @@ import { ScrollbarStyles } from '@operato/styles';
7
8
  import '../../../components/kpi-radar-chart.js';
8
9
  import '../../../components/kpi-boxplot-chart.js';
9
10
  import '../../../components/kpi-trend-chart.js';
10
- let KpiRegionPopup = class KpiRegionPopup extends LitElement {
11
+ let KpiRegionPopup = KpiRegionPopup_1 = class KpiRegionPopup extends LitElement {
11
12
  constructor() {
12
13
  super(...arguments);
13
14
  this.selectedRegion = null;
15
+ this.geoGroup = null; // 시군구 geo_group 코드 (있으면 시군구 쿼리 사용)
14
16
  this.selectedCategory = '전체 KPI';
15
17
  this.selectedChartType = 'boxplot';
16
18
  this.selectedPeriod = '월';
@@ -43,28 +45,58 @@ let KpiRegionPopup = class KpiRegionPopup extends LitElement {
43
45
  if (!this.selectedRegion)
44
46
  return;
45
47
  try {
46
- const response = await client.query({
47
- query: gql `
48
- query GetRegionKpiStats($metroArea: String!, $startYearMonth: String, $endYearMonth: String) {
49
- kpiYValueStatsByMetroArea(metroArea: $metroArea, startYearMonth: $startYearMonth, endYearMonth: $endYearMonth) {
50
- kpiName
51
- minVal
52
- q1Val
53
- medVal
54
- q3Val
55
- maxVal
56
- avgVal
57
- projectCount
48
+ let regionKpiStats;
49
+ if (this.geoGroup) {
50
+ // 시군구 모드: geo_group 코드로 조회
51
+ const response = await client.query({
52
+ query: gql `
53
+ query GetSigunguKpiStats($geoGroup: String!, $startYearMonth: String, $endYearMonth: String) {
54
+ kpiYValueStatsByGeoGroup(geoGroup: $geoGroup, startYearMonth: $startYearMonth, endYearMonth: $endYearMonth) {
55
+ kpiName
56
+ minVal
57
+ q1Val
58
+ medVal
59
+ q3Val
60
+ maxVal
61
+ avgVal
62
+ projectCount
63
+ }
58
64
  }
59
- }
60
- `,
61
- variables: {
62
- metroArea: this.selectedRegion,
63
- startYearMonth: this.startYearMonth,
64
- endYearMonth: this.endYearMonth
65
- }
66
- });
67
- this.regionKpiStats = response.data.kpiYValueStatsByMetroArea || [];
65
+ `,
66
+ variables: {
67
+ geoGroup: this.geoGroup,
68
+ startYearMonth: this.startYearMonth,
69
+ endYearMonth: this.endYearMonth
70
+ }
71
+ });
72
+ regionKpiStats = response.data.kpiYValueStatsByGeoGroup || [];
73
+ }
74
+ else {
75
+ // 광역 모드: 광역시도 이름으로 조회
76
+ const response = await client.query({
77
+ query: gql `
78
+ query GetRegionKpiStats($metroArea: String!, $startYearMonth: String, $endYearMonth: String) {
79
+ kpiYValueStatsByMetroArea(metroArea: $metroArea, startYearMonth: $startYearMonth, endYearMonth: $endYearMonth) {
80
+ kpiName
81
+ minVal
82
+ q1Val
83
+ medVal
84
+ q3Val
85
+ maxVal
86
+ avgVal
87
+ projectCount
88
+ }
89
+ }
90
+ `,
91
+ variables: {
92
+ metroArea: this.selectedRegion,
93
+ startYearMonth: this.startYearMonth,
94
+ endYearMonth: this.endYearMonth
95
+ }
96
+ });
97
+ regionKpiStats = response.data.kpiYValueStatsByMetroArea || [];
98
+ }
99
+ this.regionKpiStats = regionKpiStats;
68
100
  console.log('Region KPI Stats:', this.regionKpiStats);
69
101
  this.generateChartData();
70
102
  }
@@ -144,9 +176,12 @@ let KpiRegionPopup = class KpiRegionPopup extends LitElement {
144
176
  generateTrendData() {
145
177
  if (!this.selectedRegion)
146
178
  return;
147
- // 선택된 지역의 월별 추이 데이터 추출
179
+ // 시군구 모드인 경우 소속 광역시도의 추이 데이터 사용
180
+ const trendRegion = this.geoGroup
181
+ ? KpiRegionPopup_1.GEO_TO_METRO[this.geoGroup] || this.selectedRegion
182
+ : this.selectedRegion;
148
183
  const regionTrendData = this.monthlyTrendData
149
- .filter(d => d.geoGroup === this.selectedRegion)
184
+ .filter(d => d.geoGroup === trendRegion)
150
185
  .sort((a, b) => a.yearMonth.localeCompare(b.yearMonth));
151
186
  this.trendData = regionTrendData.map(d => {
152
187
  const value = Math.round(d.avgVal * 100); // 20배 스케일
@@ -384,10 +419,34 @@ KpiRegionPopup.styles = [
384
419
  }
385
420
  `
386
421
  ];
422
+ // geo_group → 광역시도 매핑 (시군구 모드에서 추이 데이터 조회용)
423
+ KpiRegionPopup.GEO_TO_METRO = {
424
+ '01': '서울특별시', '02': '서울특별시', '03': '서울특별시', '04': '서울특별시',
425
+ '05': '서울특별시', '06': '서울특별시', '07': '서울특별시', '08': '서울특별시',
426
+ '10': '경기도', '11': '경기도', '12': '경기도', '13': '경기도', '14': '경기도',
427
+ '15': '경기도', '16': '경기도', '17': '경기도', '18': '경기도',
428
+ '21': '인천광역시', '22': '인천광역시', '23': '인천광역시',
429
+ '24': '강원도', '25': '강원도', '26': '강원도', '33': '강원도',
430
+ '27': '충청북도', '28': '충청북도', '29': '충청북도',
431
+ '30': '세종특별자치시', '31': '충청남도', '32': '충청남도',
432
+ '34': '대전광역시', '35': '대전광역시',
433
+ '36': '경상북도', '37': '경상북도', '38': '경상북도', '39': '경상북도', '40': '경상북도',
434
+ '41': '대구광역시', '42': '대구광역시', '43': '대구광역시',
435
+ '44': '울산광역시', '45': '울산광역시',
436
+ '46': '부산광역시', '47': '부산광역시', '48': '부산광역시', '49': '부산광역시',
437
+ '50': '경상남도', '51': '경상남도', '52': '경상남도', '53': '경상남도',
438
+ '54': '전라북도', '55': '전라북도', '56': '전라북도',
439
+ '57': '전라남도', '58': '전라남도', '59': '전라남도',
440
+ '61': '광주광역시', '62': '광주광역시', '63': '제주특별자치도'
441
+ };
387
442
  __decorate([
388
443
  property({ type: String }),
389
444
  __metadata("design:type", Object)
390
445
  ], KpiRegionPopup.prototype, "selectedRegion", void 0);
446
+ __decorate([
447
+ property({ type: String }),
448
+ __metadata("design:type", Object)
449
+ ], KpiRegionPopup.prototype, "geoGroup", void 0);
391
450
  __decorate([
392
451
  property({ type: String }),
393
452
  __metadata("design:type", Object)
@@ -432,7 +491,7 @@ __decorate([
432
491
  state(),
433
492
  __metadata("design:type", Array)
434
493
  ], KpiRegionPopup.prototype, "regionKpiStats", void 0);
435
- KpiRegionPopup = __decorate([
494
+ KpiRegionPopup = KpiRegionPopup_1 = __decorate([
436
495
  customElement('kpi-region-popup')
437
496
  ], KpiRegionPopup);
438
497
  export { KpiRegionPopup };