@dssp/dkpi 1.0.0-alpha.66 → 1.0.0-alpha.68
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/dist-client/components/kpi-2d-lookup-chart.d.ts +10 -30
- package/dist-client/components/kpi-2d-lookup-chart.js +150 -362
- package/dist-client/components/kpi-2d-lookup-chart.js.map +1 -1
- package/dist-client/components/kpi-boxplot-chart.js +51 -20
- package/dist-client/components/kpi-boxplot-chart.js.map +1 -1
- package/dist-client/components/kpi-lookup-chart.d.ts +15 -4
- package/dist-client/components/kpi-lookup-chart.js +248 -292
- package/dist-client/components/kpi-lookup-chart.js.map +1 -1
- package/dist-client/components/kpi-radar-chart.js +29 -5
- package/dist-client/components/kpi-radar-chart.js.map +1 -1
- package/dist-client/components/kpi-single-boxplot-chart.js +72 -14
- package/dist-client/components/kpi-single-boxplot-chart.js.map +1 -1
- package/dist-client/google-map/common-google-map.js +10 -8
- package/dist-client/google-map/common-google-map.js.map +1 -1
- package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js +7 -0
- 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 +2 -0
- package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js +12 -4
- 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 +84 -25
- package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js.map +1 -1
- package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.d.ts +12 -0
- package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js +243 -16
- package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js.map +1 -1
- package/dist-client/pages/sv-project-detail.d.ts +1 -1
- package/dist-client/pages/sv-project-detail.js +25 -7
- package/dist-client/pages/sv-project-detail.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/service/kpi-stat/kpi-stat-query.d.ts +2 -0
- package/dist-server/service/kpi-stat/kpi-stat-query.js +120 -0
- package/dist-server/service/kpi-stat/kpi-stat-query.js.map +1 -1
- package/dist-server/service/kpi-stat/kpi-stat-types.d.ts +1 -0
- package/dist-server/service/kpi-stat/kpi-stat-types.js +4 -0
- package/dist-server/service/kpi-stat/kpi-stat-types.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/schema.graphql +7 -0
|
@@ -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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
|
|
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 ===
|
|
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 };
|
|
@@ -1 +1 @@
|
|
|
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 }) 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 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\n this.regionKpiStats = response.data.kpiYValueStatsByMetroArea || []\n console.log('Region KPI Stats:', this.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 private generateTrendData() {\n if (!this.selectedRegion) return\n\n // 선택된 지역의 월별 추이 데이터 추출\n const regionTrendData = this.monthlyTrendData\n .filter(d => d.geoGroup === this.selectedRegion)\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 }[] = []\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 console.log('Region KPI Stats:', this.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"]}
|
|
@@ -10,6 +10,9 @@ export declare class KpiDashboardMapPage extends PageView {
|
|
|
10
10
|
endYearMonth: string;
|
|
11
11
|
selectedSectorType: string;
|
|
12
12
|
selectedBuildingUsage: string;
|
|
13
|
+
currentZoom: number;
|
|
14
|
+
sigunguData: any[];
|
|
15
|
+
private static readonly SIGUNGU_ZOOM_THRESHOLD;
|
|
13
16
|
nationalData: any;
|
|
14
17
|
seoulData: any;
|
|
15
18
|
mapData: any;
|
|
@@ -17,6 +20,7 @@ export declare class KpiDashboardMapPage extends PageView {
|
|
|
17
20
|
totalKpiYStats: any[];
|
|
18
21
|
regionalKpiStats: any[];
|
|
19
22
|
selectedRegion: string | null;
|
|
23
|
+
selectedGeoGroup: string | null;
|
|
20
24
|
showRegionPopup: boolean;
|
|
21
25
|
connectedCallback(): void;
|
|
22
26
|
private initializeDefaultPeriod;
|
|
@@ -26,6 +30,14 @@ export declare class KpiDashboardMapPage extends PageView {
|
|
|
26
30
|
private generateMapDataFromKpiValues;
|
|
27
31
|
private generateMapDataFromStatistics;
|
|
28
32
|
private generateMapDataFromRegionalStats;
|
|
33
|
+
private static readonly SIGUNGU_MAP;
|
|
34
|
+
private fetchSigunguData;
|
|
35
|
+
private static readonly GEO_TO_METRO;
|
|
36
|
+
private calculateChangeRate;
|
|
37
|
+
private generateSigunguMapData;
|
|
38
|
+
private onZoomChanged;
|
|
39
|
+
private static readonly METRO_BOUNDS;
|
|
40
|
+
private focusMapOnRegion;
|
|
29
41
|
private downloadExcel;
|
|
30
42
|
private onRegionClick;
|
|
31
43
|
private closeRegionPopup;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
var KpiDashboardMapPage_1;
|
|
1
2
|
import { __decorate, __metadata } from "tslib";
|
|
2
3
|
import gql from 'graphql-tag';
|
|
3
4
|
import { html, css, nothing } from 'lit';
|
|
@@ -9,7 +10,7 @@ import { notify } from '@operato/layout';
|
|
|
9
10
|
import './components/kpi-map-panel';
|
|
10
11
|
import './components/kpi-left-panel';
|
|
11
12
|
import './components/kpi-region-popup';
|
|
12
|
-
let KpiDashboardMapPage = class KpiDashboardMapPage extends PageView {
|
|
13
|
+
let KpiDashboardMapPage = KpiDashboardMapPage_1 = class KpiDashboardMapPage extends PageView {
|
|
13
14
|
constructor() {
|
|
14
15
|
super(...arguments);
|
|
15
16
|
this.selectedCategory = '전체 KPI';
|
|
@@ -18,6 +19,8 @@ let KpiDashboardMapPage = class KpiDashboardMapPage extends PageView {
|
|
|
18
19
|
this.endYearMonth = ''; // YYYY-MM 형식
|
|
19
20
|
this.selectedSectorType = ''; // PUBLIC | PRIVATE
|
|
20
21
|
this.selectedBuildingUsage = ''; // RESIDENTIAL | NON_RESIDENTIAL
|
|
22
|
+
this.currentZoom = 7.2;
|
|
23
|
+
this.sigunguData = []; // 시군구 단위 데이터
|
|
21
24
|
this.nationalData = null;
|
|
22
25
|
this.seoulData = null;
|
|
23
26
|
this.mapData = null;
|
|
@@ -26,6 +29,7 @@ let KpiDashboardMapPage = class KpiDashboardMapPage extends PageView {
|
|
|
26
29
|
this.regionalKpiStats = []; // 지역별 KPI 통계
|
|
27
30
|
// 팝업 관련 상태 추가
|
|
28
31
|
this.selectedRegion = null;
|
|
32
|
+
this.selectedGeoGroup = null; // 시군구 geo_group 코드
|
|
29
33
|
this.showRegionPopup = false;
|
|
30
34
|
}
|
|
31
35
|
connectedCallback() {
|
|
@@ -334,19 +338,7 @@ let KpiDashboardMapPage = class KpiDashboardMapPage extends PageView {
|
|
|
334
338
|
};
|
|
335
339
|
this.mapData = Object.keys(regionCoordinates).map(regionName => {
|
|
336
340
|
const stat = this.regionalKpiStats.find(s => s.geoGroup === regionName);
|
|
337
|
-
|
|
338
|
-
const regionMonthlyData = this.monthlyTrendData
|
|
339
|
-
.filter(d => d.geoGroup === regionName)
|
|
340
|
-
.sort((a, b) => a.yearMonth.localeCompare(b.yearMonth));
|
|
341
|
-
// 변동율 계산: 가장 오래된 달과 가장 최근 달 비교
|
|
342
|
-
let changeRate = 0;
|
|
343
|
-
if (regionMonthlyData.length >= 2) {
|
|
344
|
-
const oldestValue = regionMonthlyData[0].avgVal;
|
|
345
|
-
const latestValue = regionMonthlyData[regionMonthlyData.length - 1].avgVal;
|
|
346
|
-
if (oldestValue !== 0) {
|
|
347
|
-
changeRate = ((latestValue - oldestValue) / oldestValue) * 100;
|
|
348
|
-
}
|
|
349
|
-
}
|
|
341
|
+
const changeRate = this.calculateChangeRate(regionName);
|
|
350
342
|
return {
|
|
351
343
|
region: regionName,
|
|
352
344
|
kpi: stat ? (stat.avgVal * 100).toFixed(1) : '0.0',
|
|
@@ -359,12 +351,123 @@ let KpiDashboardMapPage = class KpiDashboardMapPage extends PageView {
|
|
|
359
351
|
});
|
|
360
352
|
console.log('Generated map data from regional stats:', this.mapData.length, 'regions');
|
|
361
353
|
}
|
|
354
|
+
async fetchSigunguData() {
|
|
355
|
+
try {
|
|
356
|
+
const categoryToKpiName = {
|
|
357
|
+
'전체 KPI': null,
|
|
358
|
+
'일정 성과': 'Y1. 일정성과',
|
|
359
|
+
'비용 성과': 'Y2. 비용성과',
|
|
360
|
+
'품질 성과': 'Y3. 품질성과',
|
|
361
|
+
'안전 성과': 'Y4. 안전성과',
|
|
362
|
+
'환경 성과': 'Y5. 환경성과',
|
|
363
|
+
'생산성 성과': 'Y6. 생산성성과'
|
|
364
|
+
};
|
|
365
|
+
const kpiName = categoryToKpiName[this.selectedCategory];
|
|
366
|
+
const response = await client.query({
|
|
367
|
+
query: gql `
|
|
368
|
+
query GetSigunguStats(
|
|
369
|
+
$kpiName: String
|
|
370
|
+
$startYearMonth: String
|
|
371
|
+
$endYearMonth: String
|
|
372
|
+
$sectorType: String
|
|
373
|
+
$buildingUsage: String
|
|
374
|
+
) {
|
|
375
|
+
kpiZValueStatsBySigungu(
|
|
376
|
+
kpiName: $kpiName
|
|
377
|
+
startYearMonth: $startYearMonth
|
|
378
|
+
endYearMonth: $endYearMonth
|
|
379
|
+
sectorType: $sectorType
|
|
380
|
+
buildingUsage: $buildingUsage
|
|
381
|
+
) {
|
|
382
|
+
geoGroup
|
|
383
|
+
avgVal
|
|
384
|
+
minVal
|
|
385
|
+
maxVal
|
|
386
|
+
projectCount
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
`,
|
|
390
|
+
variables: {
|
|
391
|
+
kpiName,
|
|
392
|
+
startYearMonth: this.startYearMonth || null,
|
|
393
|
+
endYearMonth: this.endYearMonth || null,
|
|
394
|
+
sectorType: this.selectedSectorType || null,
|
|
395
|
+
buildingUsage: this.selectedBuildingUsage || null
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
this.sigunguData = response.data.kpiZValueStatsBySigungu || [];
|
|
399
|
+
this.generateSigunguMapData();
|
|
400
|
+
}
|
|
401
|
+
catch (e) {
|
|
402
|
+
console.error('시군구 데이터 조회 실패:', e);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
calculateChangeRate(metroName) {
|
|
406
|
+
const regionMonthlyData = this.monthlyTrendData
|
|
407
|
+
.filter((d) => d.geoGroup === metroName)
|
|
408
|
+
.sort((a, b) => a.yearMonth.localeCompare(b.yearMonth));
|
|
409
|
+
if (regionMonthlyData.length >= 2) {
|
|
410
|
+
const oldest = regionMonthlyData[0].avgVal;
|
|
411
|
+
const latest = regionMonthlyData[regionMonthlyData.length - 1].avgVal;
|
|
412
|
+
if (oldest !== 0) {
|
|
413
|
+
return ((latest - oldest) / oldest) * 100;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
return 0;
|
|
417
|
+
}
|
|
418
|
+
generateSigunguMapData() {
|
|
419
|
+
const map = KpiDashboardMapPage_1.SIGUNGU_MAP;
|
|
420
|
+
const geoToMetro = KpiDashboardMapPage_1.GEO_TO_METRO;
|
|
421
|
+
this.mapData = this.sigunguData
|
|
422
|
+
.filter((s) => s.geoGroup && map[s.geoGroup])
|
|
423
|
+
.map((s) => {
|
|
424
|
+
const info = map[s.geoGroup];
|
|
425
|
+
const metro = geoToMetro[s.geoGroup] || '';
|
|
426
|
+
const changeRate = this.calculateChangeRate(metro);
|
|
427
|
+
return {
|
|
428
|
+
region: info.name,
|
|
429
|
+
kpi: (s.avgVal * 100).toFixed(1),
|
|
430
|
+
change: changeRate.toFixed(2),
|
|
431
|
+
trend: changeRate >= 0 ? 'up' : 'down',
|
|
432
|
+
lat: info.lat,
|
|
433
|
+
lng: info.lng,
|
|
434
|
+
dataCount: s.projectCount || 0
|
|
435
|
+
};
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
onZoomChanged(e) {
|
|
439
|
+
const newZoom = e.detail.zoom;
|
|
440
|
+
const threshold = KpiDashboardMapPage_1.SIGUNGU_ZOOM_THRESHOLD;
|
|
441
|
+
const wasAbove = this.currentZoom >= threshold;
|
|
442
|
+
const isAbove = newZoom >= threshold;
|
|
443
|
+
this.currentZoom = newZoom;
|
|
444
|
+
// 줌 레벨이 threshold를 넘나들 때만 데이터 전환
|
|
445
|
+
if (wasAbove !== isAbove) {
|
|
446
|
+
if (isAbove) {
|
|
447
|
+
this.fetchSigunguData();
|
|
448
|
+
}
|
|
449
|
+
else {
|
|
450
|
+
this.generateMapDataFromRegionalStats();
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
focusMapOnRegion(regionName) {
|
|
455
|
+
var _a;
|
|
456
|
+
const bounds = KpiDashboardMapPage_1.METRO_BOUNDS[regionName];
|
|
457
|
+
if (!bounds)
|
|
458
|
+
return;
|
|
459
|
+
const mapPanel = (_a = this.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector('kpi-map-panel');
|
|
460
|
+
mapPanel === null || mapPanel === void 0 ? void 0 : mapPanel.focusOnLocation(bounds.lat, bounds.lng, bounds.zoom);
|
|
461
|
+
}
|
|
362
462
|
downloadExcel() {
|
|
363
|
-
// 엑셀 다운로드 기능
|
|
364
463
|
console.log('엑셀 다운로드');
|
|
365
464
|
}
|
|
366
465
|
onRegionClick(region) {
|
|
367
466
|
this.selectedRegion = region;
|
|
467
|
+
// 시군구 모드인 경우 region name → geo_group 코드 역매핑
|
|
468
|
+
const sigunguMap = KpiDashboardMapPage_1.SIGUNGU_MAP;
|
|
469
|
+
const entry = Object.entries(sigunguMap).find(([, v]) => v.name === region);
|
|
470
|
+
this.selectedGeoGroup = entry ? entry[0] : null;
|
|
368
471
|
this.showRegionPopup = true;
|
|
369
472
|
// fetchRegionData는 더 이상 필요하지 않음 (kpi-region-popup에서 직접 데이터 가져옴)
|
|
370
473
|
// this.fetchRegionData(region)
|
|
@@ -445,6 +548,9 @@ let KpiDashboardMapPage = class KpiDashboardMapPage extends PageView {
|
|
|
445
548
|
}}
|
|
446
549
|
@region-click=${(e) => {
|
|
447
550
|
this.onRegionClick(e.detail.region);
|
|
551
|
+
}}
|
|
552
|
+
@focus-region=${(e) => {
|
|
553
|
+
this.focusMapOnRegion(e.detail.region);
|
|
448
554
|
}}
|
|
449
555
|
@monthly-trend-data-loaded=${(e) => {
|
|
450
556
|
this.monthlyTrendData = e.detail.data;
|
|
@@ -467,6 +573,9 @@ let KpiDashboardMapPage = class KpiDashboardMapPage extends PageView {
|
|
|
467
573
|
.mapData=${this.mapData}
|
|
468
574
|
@region-click=${(e) => {
|
|
469
575
|
this.onRegionClick(e.detail.region);
|
|
576
|
+
}}
|
|
577
|
+
@zoom-changed=${(e) => {
|
|
578
|
+
this.onZoomChanged(e);
|
|
470
579
|
}}
|
|
471
580
|
></kpi-map-panel>
|
|
472
581
|
|
|
@@ -475,6 +584,7 @@ let KpiDashboardMapPage = class KpiDashboardMapPage extends PageView {
|
|
|
475
584
|
? html `
|
|
476
585
|
<kpi-region-popup
|
|
477
586
|
.selectedRegion=${this.selectedRegion}
|
|
587
|
+
.geoGroup=${this.selectedGeoGroup}
|
|
478
588
|
.selectedCategory=${this.selectedCategory}
|
|
479
589
|
.monthlyTrendData=${this.monthlyTrendData}
|
|
480
590
|
.totalKpiStats=${this.totalKpiYStats}
|
|
@@ -512,6 +622,111 @@ KpiDashboardMapPage.styles = [
|
|
|
512
622
|
}
|
|
513
623
|
`
|
|
514
624
|
];
|
|
625
|
+
KpiDashboardMapPage.SIGUNGU_ZOOM_THRESHOLD = 10;
|
|
626
|
+
// 시군구별 좌표 (geo_group 2자리 코드 → { name, lat, lng })
|
|
627
|
+
KpiDashboardMapPage.SIGUNGU_MAP = {
|
|
628
|
+
'01': { name: '서울 종로구', lat: 37.5735, lng: 126.9790 },
|
|
629
|
+
'02': { name: '서울 중구', lat: 37.5641, lng: 126.9979 },
|
|
630
|
+
'03': { name: '서울 용산구', lat: 37.5326, lng: 126.9906 },
|
|
631
|
+
'04': { name: '서울 성동구', lat: 37.5634, lng: 127.0369 },
|
|
632
|
+
'05': { name: '서울 광진구', lat: 37.5385, lng: 127.0823 },
|
|
633
|
+
'06': { name: '서울 강남구', lat: 37.5172, lng: 127.0473 },
|
|
634
|
+
'07': { name: '서울 송파구', lat: 37.5145, lng: 127.1060 },
|
|
635
|
+
'08': { name: '서울 강서구', lat: 37.5510, lng: 126.8495 },
|
|
636
|
+
'10': { name: '수원시', lat: 37.2636, lng: 127.0286 },
|
|
637
|
+
'11': { name: '성남시', lat: 37.4200, lng: 127.1265 },
|
|
638
|
+
'12': { name: '고양시', lat: 37.6584, lng: 126.8320 },
|
|
639
|
+
'13': { name: '용인시', lat: 37.2411, lng: 127.1776 },
|
|
640
|
+
'14': { name: '부천시', lat: 37.5034, lng: 126.7660 },
|
|
641
|
+
'15': { name: '안산시', lat: 37.3219, lng: 126.8309 },
|
|
642
|
+
'16': { name: '화성시', lat: 37.1995, lng: 126.8312 },
|
|
643
|
+
'17': { name: '평택시', lat: 36.9921, lng: 127.1127 },
|
|
644
|
+
'18': { name: '파주시', lat: 37.7599, lng: 126.7799 },
|
|
645
|
+
'21': { name: '인천 남동구', lat: 37.4488, lng: 126.7308 },
|
|
646
|
+
'22': { name: '인천 부평구', lat: 37.5076, lng: 126.7218 },
|
|
647
|
+
'23': { name: '인천 서구', lat: 37.5458, lng: 126.6760 },
|
|
648
|
+
'24': { name: '춘천시', lat: 37.8813, lng: 127.7300 },
|
|
649
|
+
'25': { name: '원주시', lat: 37.3422, lng: 127.9202 },
|
|
650
|
+
'26': { name: '강릉시', lat: 37.7519, lng: 128.8761 },
|
|
651
|
+
'27': { name: '청주시', lat: 36.6424, lng: 127.4890 },
|
|
652
|
+
'28': { name: '충주시', lat: 36.9910, lng: 127.9259 },
|
|
653
|
+
'29': { name: '제천시', lat: 37.1327, lng: 128.1910 },
|
|
654
|
+
'30': { name: '세종시', lat: 36.4875, lng: 127.2816 },
|
|
655
|
+
'31': { name: '천안시', lat: 36.8151, lng: 127.1139 },
|
|
656
|
+
'32': { name: '아산시', lat: 36.7898, lng: 127.0018 },
|
|
657
|
+
'33': { name: '속초시', lat: 38.2071, lng: 128.5918 },
|
|
658
|
+
'34': { name: '대전 서구', lat: 36.3555, lng: 127.3835 },
|
|
659
|
+
'35': { name: '대전 유성구', lat: 36.3622, lng: 127.3561 },
|
|
660
|
+
'36': { name: '포항시', lat: 36.0190, lng: 129.3435 },
|
|
661
|
+
'37': { name: '경주시', lat: 35.8562, lng: 129.2247 },
|
|
662
|
+
'38': { name: '구미시', lat: 36.1196, lng: 128.3443 },
|
|
663
|
+
'39': { name: '안동시', lat: 36.5684, lng: 128.7294 },
|
|
664
|
+
'40': { name: '김천시', lat: 36.1398, lng: 128.1136 },
|
|
665
|
+
'41': { name: '대구 중구', lat: 35.8693, lng: 128.6062 },
|
|
666
|
+
'42': { name: '대구 수성구', lat: 35.8584, lng: 128.6308 },
|
|
667
|
+
'43': { name: '대구 달서구', lat: 35.8299, lng: 128.5327 },
|
|
668
|
+
'44': { name: '울산 남구', lat: 35.5444, lng: 129.3304 },
|
|
669
|
+
'45': { name: '울산 중구', lat: 35.5696, lng: 129.3324 },
|
|
670
|
+
'46': { name: '부산 중구', lat: 35.1060, lng: 129.0324 },
|
|
671
|
+
'47': { name: '부산 해운대구', lat: 35.1631, lng: 129.1636 },
|
|
672
|
+
'48': { name: '부산 사하구', lat: 35.1046, lng: 128.9751 },
|
|
673
|
+
'49': { name: '부산 금정구', lat: 35.2431, lng: 129.0923 },
|
|
674
|
+
'50': { name: '창원시', lat: 35.2281, lng: 128.6812 },
|
|
675
|
+
'51': { name: '김해시', lat: 35.2285, lng: 128.8894 },
|
|
676
|
+
'52': { name: '진주시', lat: 35.1798, lng: 128.1076 },
|
|
677
|
+
'53': { name: '양산시', lat: 35.3350, lng: 129.0373 },
|
|
678
|
+
'54': { name: '전주시', lat: 35.8242, lng: 127.1480 },
|
|
679
|
+
'55': { name: '익산시', lat: 35.9483, lng: 126.9576 },
|
|
680
|
+
'56': { name: '군산시', lat: 35.9676, lng: 126.7369 },
|
|
681
|
+
'57': { name: '목포시', lat: 34.8118, lng: 126.3922 },
|
|
682
|
+
'58': { name: '여수시', lat: 34.7604, lng: 127.6622 },
|
|
683
|
+
'59': { name: '순천시', lat: 34.9506, lng: 127.4873 },
|
|
684
|
+
'61': { name: '광주 서구', lat: 35.1525, lng: 126.8895 },
|
|
685
|
+
'62': { name: '광주 북구', lat: 35.1744, lng: 126.9120 },
|
|
686
|
+
'63': { name: '제주시', lat: 33.4996, lng: 126.5312 }
|
|
687
|
+
};
|
|
688
|
+
// geo_group 코드 → 광역시도 매핑
|
|
689
|
+
KpiDashboardMapPage.GEO_TO_METRO = {
|
|
690
|
+
'01': '서울특별시', '02': '서울특별시', '03': '서울특별시', '04': '서울특별시',
|
|
691
|
+
'05': '서울특별시', '06': '서울특별시', '07': '서울특별시', '08': '서울특별시',
|
|
692
|
+
'10': '경기도', '11': '경기도', '12': '경기도', '13': '경기도', '14': '경기도',
|
|
693
|
+
'15': '경기도', '16': '경기도', '17': '경기도', '18': '경기도',
|
|
694
|
+
'21': '인천광역시', '22': '인천광역시', '23': '인천광역시',
|
|
695
|
+
'24': '강원도', '25': '강원도', '26': '강원도', '33': '강원도',
|
|
696
|
+
'27': '충청북도', '28': '충청북도', '29': '충청북도',
|
|
697
|
+
'30': '세종특별자치시',
|
|
698
|
+
'31': '충청남도', '32': '충청남도',
|
|
699
|
+
'34': '대전광역시', '35': '대전광역시',
|
|
700
|
+
'36': '경상북도', '37': '경상북도', '38': '경상북도', '39': '경상북도', '40': '경상북도',
|
|
701
|
+
'41': '대구광역시', '42': '대구광역시', '43': '대구광역시',
|
|
702
|
+
'44': '울산광역시', '45': '울산광역시',
|
|
703
|
+
'46': '부산광역시', '47': '부산광역시', '48': '부산광역시', '49': '부산광역시',
|
|
704
|
+
'50': '경상남도', '51': '경상남도', '52': '경상남도', '53': '경상남도',
|
|
705
|
+
'54': '전라북도', '55': '전라북도', '56': '전라북도',
|
|
706
|
+
'57': '전라남도', '58': '전라남도', '59': '전라남도',
|
|
707
|
+
'61': '광주광역시', '62': '광주광역시',
|
|
708
|
+
'63': '제주특별자치도'
|
|
709
|
+
};
|
|
710
|
+
// 광역시도별 중심 좌표 + 줌 레벨 (시군구가 모두 보이는 수준)
|
|
711
|
+
KpiDashboardMapPage.METRO_BOUNDS = {
|
|
712
|
+
서울특별시: { lat: 37.5665, lng: 126.978, zoom: 11 },
|
|
713
|
+
부산광역시: { lat: 35.1796, lng: 129.0756, zoom: 11 },
|
|
714
|
+
대구광역시: { lat: 35.8714, lng: 128.6014, zoom: 11 },
|
|
715
|
+
인천광역시: { lat: 37.4563, lng: 126.7052, zoom: 11 },
|
|
716
|
+
광주광역시: { lat: 35.1595, lng: 126.8526, zoom: 12 },
|
|
717
|
+
대전광역시: { lat: 36.3504, lng: 127.3845, zoom: 12 },
|
|
718
|
+
울산광역시: { lat: 35.5384, lng: 129.3114, zoom: 11 },
|
|
719
|
+
세종특별자치시: { lat: 36.4875, lng: 127.2816, zoom: 12 },
|
|
720
|
+
경기도: { lat: 37.4138, lng: 127.0183, zoom: 9 },
|
|
721
|
+
강원도: { lat: 37.8228, lng: 128.1555, zoom: 9 },
|
|
722
|
+
충청북도: { lat: 36.8, lng: 127.7, zoom: 9 },
|
|
723
|
+
충청남도: { lat: 36.5184, lng: 126.8, zoom: 10 },
|
|
724
|
+
전라북도: { lat: 35.7175, lng: 127.153, zoom: 10 },
|
|
725
|
+
전라남도: { lat: 34.8679, lng: 126.991, zoom: 9 },
|
|
726
|
+
경상북도: { lat: 36.4919, lng: 128.8889, zoom: 9 },
|
|
727
|
+
경상남도: { lat: 35.4606, lng: 128.2132, zoom: 9 },
|
|
728
|
+
제주특별자치도: { lat: 33.4996, lng: 126.5312, zoom: 11 }
|
|
729
|
+
};
|
|
515
730
|
__decorate([
|
|
516
731
|
state(),
|
|
517
732
|
__metadata("design:type", Object)
|
|
@@ -536,6 +751,14 @@ __decorate([
|
|
|
536
751
|
state(),
|
|
537
752
|
__metadata("design:type", Object)
|
|
538
753
|
], KpiDashboardMapPage.prototype, "selectedBuildingUsage", void 0);
|
|
754
|
+
__decorate([
|
|
755
|
+
state(),
|
|
756
|
+
__metadata("design:type", Object)
|
|
757
|
+
], KpiDashboardMapPage.prototype, "currentZoom", void 0);
|
|
758
|
+
__decorate([
|
|
759
|
+
state(),
|
|
760
|
+
__metadata("design:type", Array)
|
|
761
|
+
], KpiDashboardMapPage.prototype, "sigunguData", void 0);
|
|
539
762
|
__decorate([
|
|
540
763
|
state(),
|
|
541
764
|
__metadata("design:type", Object)
|
|
@@ -564,11 +787,15 @@ __decorate([
|
|
|
564
787
|
state(),
|
|
565
788
|
__metadata("design:type", Object)
|
|
566
789
|
], KpiDashboardMapPage.prototype, "selectedRegion", void 0);
|
|
790
|
+
__decorate([
|
|
791
|
+
state(),
|
|
792
|
+
__metadata("design:type", Object)
|
|
793
|
+
], KpiDashboardMapPage.prototype, "selectedGeoGroup", void 0);
|
|
567
794
|
__decorate([
|
|
568
795
|
state(),
|
|
569
796
|
__metadata("design:type", Object)
|
|
570
797
|
], KpiDashboardMapPage.prototype, "showRegionPopup", void 0);
|
|
571
|
-
KpiDashboardMapPage = __decorate([
|
|
798
|
+
KpiDashboardMapPage = KpiDashboardMapPage_1 = __decorate([
|
|
572
799
|
customElement('kpi-dashboard-map')
|
|
573
800
|
], KpiDashboardMapPage);
|
|
574
801
|
export { KpiDashboardMapPage };
|