@dssp/dkpi 1.0.27 → 1.0.30

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 (56) hide show
  1. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.d.ts +2 -0
  2. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js +19 -4
  3. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js.map +1 -1
  4. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.d.ts +2 -0
  5. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js +19 -4
  6. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js.map +1 -1
  7. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.d.ts +2 -0
  8. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js +19 -4
  9. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js.map +1 -1
  10. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.d.ts +2 -0
  11. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js +78 -4
  12. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js.map +1 -1
  13. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.d.ts +2 -0
  14. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js +35 -4
  15. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js.map +1 -1
  16. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.d.ts +2 -0
  17. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js +29 -1
  18. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js.map +1 -1
  19. package/dist-client/pages/kpi-dashboard/kpi-dashboard.d.ts +11 -0
  20. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js +65 -12
  21. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js.map +1 -1
  22. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.d.ts +18 -0
  23. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js +138 -64
  24. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js.map +1 -1
  25. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.js +80 -8
  26. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.js.map +1 -1
  27. package/dist-client/pages/sv-project-detail.d.ts +33 -0
  28. package/dist-client/pages/sv-project-detail.js +130 -8
  29. package/dist-client/pages/sv-project-detail.js.map +1 -1
  30. package/dist-client/pages/sv-project-update.js +98 -31
  31. package/dist-client/pages/sv-project-update.js.map +1 -1
  32. package/dist-client/shared/complete-api.d.ts +7 -0
  33. package/dist-client/shared/complete-api.js +27 -0
  34. package/dist-client/shared/complete-api.js.map +1 -1
  35. package/dist-client/shared/project-axis.d.ts +23 -0
  36. package/dist-client/shared/project-axis.js +42 -0
  37. package/dist-client/shared/project-axis.js.map +1 -0
  38. package/dist-client/shared/sentiment-index.d.ts +47 -0
  39. package/dist-client/shared/sentiment-index.js +119 -0
  40. package/dist-client/shared/sentiment-index.js.map +1 -0
  41. package/dist-client/tsconfig.tsbuildinfo +1 -1
  42. package/dist-server/index.js +14 -0
  43. package/dist-server/index.js.map +1 -1
  44. package/dist-server/scripts/load-grade-data-migration.js +17 -3
  45. package/dist-server/scripts/load-grade-data-migration.js.map +1 -1
  46. package/dist-server/scripts/propagate-parent-kpi-values.js +13 -2
  47. package/dist-server/scripts/propagate-parent-kpi-values.js.map +1 -1
  48. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.d.ts +22 -0
  49. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js +93 -1
  50. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js.map +1 -1
  51. package/dist-server/service/kpi-stat/kpi-stat-query.d.ts +9 -9
  52. package/dist-server/service/kpi-stat/kpi-stat-query.js +71 -84
  53. package/dist-server/service/kpi-stat/kpi-stat-query.js.map +1 -1
  54. package/dist-server/tsconfig.tsbuildinfo +1 -1
  55. package/package.json +5 -5
  56. package/schema.graphql +2011 -67
@@ -3,6 +3,8 @@ export declare class KpiLevel1Card extends LitElement {
3
3
  static styles: import("lit").CSSResult;
4
4
  sectorType: string;
5
5
  buildingUsage: string;
6
+ projectScale: string;
7
+ facilityType: string;
6
8
  loading: boolean;
7
9
  error: string;
8
10
  totalScore: number;
@@ -9,6 +9,8 @@ let KpiLevel1Card = class KpiLevel1Card extends LitElement {
9
9
  super(...arguments);
10
10
  this.sectorType = '';
11
11
  this.buildingUsage = '';
12
+ this.projectScale = '';
13
+ this.facilityType = '';
12
14
  this.loading = true;
13
15
  this.error = '';
14
16
  this.totalScore = 0;
@@ -22,7 +24,10 @@ let KpiLevel1Card = class KpiLevel1Card extends LitElement {
22
24
  this.fetchTotalScore();
23
25
  }
24
26
  updated(changedProperties) {
25
- if (changedProperties.has('sectorType') || changedProperties.has('buildingUsage')) {
27
+ if (changedProperties.has('sectorType') ||
28
+ changedProperties.has('buildingUsage') ||
29
+ changedProperties.has('projectScale') ||
30
+ changedProperties.has('facilityType')) {
26
31
  this.fetchTotalScore();
27
32
  }
28
33
  }
@@ -53,8 +58,8 @@ let KpiLevel1Card = class KpiLevel1Card extends LitElement {
53
58
  try {
54
59
  const response = await client.query({
55
60
  query: gql `
56
- query KpiLevel1Stats($sectorType: String, $buildingUsage: String) {
57
- kpiProjectStats(kpiNamePattern: "Z", sectorType: $sectorType, buildingUsage: $buildingUsage) {
61
+ query KpiLevel1Stats($sectorType: String, $buildingUsage: String, $projectScale: String, $facilityType: String) {
62
+ kpiProjectStats(kpiNamePattern: "Z", sectorType: $sectorType, buildingUsage: $buildingUsage, projectScale: $projectScale, facilityType: $facilityType) {
58
63
  kpiName
59
64
  avgVal
60
65
  minVal
@@ -65,7 +70,9 @@ let KpiLevel1Card = class KpiLevel1Card extends LitElement {
65
70
  `,
66
71
  variables: {
67
72
  sectorType: this.sectorType || null,
68
- buildingUsage: this.buildingUsage || null
73
+ buildingUsage: this.buildingUsage || null,
74
+ projectScale: this.projectScale || null,
75
+ facilityType: this.facilityType || null
69
76
  }
70
77
  });
71
78
  const stats = response.data.kpiProjectStats || [];
@@ -224,6 +231,14 @@ __decorate([
224
231
  property({ type: String }),
225
232
  __metadata("design:type", String)
226
233
  ], KpiLevel1Card.prototype, "buildingUsage", void 0);
234
+ __decorate([
235
+ property({ type: String }),
236
+ __metadata("design:type", String)
237
+ ], KpiLevel1Card.prototype, "projectScale", void 0);
238
+ __decorate([
239
+ property({ type: String }),
240
+ __metadata("design:type", String)
241
+ ], KpiLevel1Card.prototype, "facilityType", void 0);
227
242
  __decorate([
228
243
  state(),
229
244
  __metadata("design:type", Object)
@@ -1 +1 @@
1
- {"version":3,"file":"kpi-level1-card.js","sourceRoot":"","sources":["../../../../client/pages/kpi-dashboard/cards/kpi-level1-card.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, css } from 'lit'\nimport { customElement, state, property } from 'lit/decorators.js'\nimport { LitElement } from 'lit'\nimport { client } from '@operato/graphql'\nimport gql from 'graphql-tag'\n\n@customElement('kpi-level1-card')\nexport class KpiLevel1Card extends LitElement {\n static styles = css`\n :host {\n display: block;\n }\n .score-card {\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n border-radius: 16px;\n padding: 32px;\n color: white;\n box-shadow: 0 8px 32px rgba(102, 126, 234, 0.3);\n text-align: center;\n position: relative;\n overflow: hidden;\n }\n .score-card::before {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(255, 255, 255, 0.1);\n border-radius: 16px;\n }\n .score-content {\n position: relative;\n z-index: 1;\n }\n .score-title {\n font-size: 1.2rem;\n font-weight: 600;\n margin-bottom: 16px;\n opacity: 0.9;\n }\n .score-value {\n font-size: 4rem;\n font-weight: bold;\n margin-bottom: 8px;\n text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);\n }\n .score-grade {\n font-size: 1.5rem;\n font-weight: 600;\n margin-bottom: 16px;\n opacity: 0.9;\n }\n .score-period {\n font-size: 0.9rem;\n opacity: 0.7;\n margin-bottom: 16px;\n }\n .score-details {\n display: flex;\n justify-content: space-around;\n margin-top: 24px;\n padding-top: 16px;\n border-top: 1px solid rgba(255, 255, 255, 0.2);\n }\n .detail-item {\n text-align: center;\n }\n .detail-value {\n font-size: 1.4rem;\n font-weight: bold;\n margin-bottom: 4px;\n }\n .detail-label {\n font-size: 0.8rem;\n opacity: 0.8;\n }\n .loading {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 200px;\n color: #666;\n font-size: 1.1rem;\n }\n .error {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 200px;\n color: #d32f2f;\n font-size: 1.1rem;\n }\n `\n\n @property({ type: String }) sectorType: string = ''\n @property({ type: String }) buildingUsage: string = ''\n\n @state() loading = true\n @state() error = ''\n @state() totalScore = 0\n @state() grade = ''\n @state() projectCount = 0\n @state() minScore = 0\n @state() maxScore = 0\n\n connectedCallback() {\n super.connectedCallback()\n this.fetchTotalScore()\n }\n\n override updated(changedProperties: Map<string, unknown>) {\n if (changedProperties.has('sectorType') || changedProperties.has('buildingUsage')) {\n this.fetchTotalScore()\n }\n }\n\n private calculateGrade(score: number): string {\n if (score >= 90) return 'A+'\n if (score >= 85) return 'A'\n if (score >= 80) return 'A-'\n if (score >= 75) return 'B+'\n if (score >= 70) return 'B'\n if (score >= 65) return 'B-'\n if (score >= 60) return 'C+'\n if (score >= 55) return 'C'\n if (score >= 50) return 'C-'\n return 'D'\n }\n\n async fetchTotalScore() {\n this.loading = true\n this.error = ''\n\n try {\n const response = await client.query({\n query: gql`\n query KpiLevel1Stats($sectorType: String, $buildingUsage: String) {\n kpiProjectStats(kpiNamePattern: \"Z\", sectorType: $sectorType, buildingUsage: $buildingUsage) {\n kpiName\n avgVal\n minVal\n maxVal\n projectCount\n }\n }\n `,\n variables: {\n sectorType: this.sectorType || null,\n buildingUsage: this.buildingUsage || null\n }\n })\n\n const stats: any[] = response.data.kpiProjectStats || []\n\n if (stats.length === 0) {\n this.error = '데이터가 없습니다.'\n return\n }\n\n const zStat = stats[0]\n this.totalScore = Math.round(zStat.avgVal * 100)\n this.grade = this.calculateGrade(this.totalScore)\n this.projectCount = zStat.projectCount\n this.minScore = Math.round(zStat.minVal * 100)\n this.maxScore = Math.round(zStat.maxVal * 100)\n } catch (e) {\n console.error('총점 데이터를 불러오지 못했습니다:', e)\n this.error = '총점 데이터를 불러오지 못했습니다.'\n } finally {\n this.loading = false\n }\n }\n\n render() {\n if (this.loading) {\n return html`\n <div class=\"score-card\">\n <div class=\"loading\">총점 계산 중...</div>\n </div>\n `\n }\n\n if (this.error) {\n return html`\n <div class=\"score-card\">\n <div class=\"error\">${this.error}</div>\n </div>\n `\n }\n\n return html`\n <div class=\"score-card\">\n <div class=\"score-content\">\n <div class=\"score-title\">그룹 총점</div>\n <div class=\"score-value\">${this.totalScore}</div>\n <div class=\"score-grade\">${this.grade}</div>\n\n <div class=\"score-details\">\n <div class=\"detail-item\">\n <div class=\"detail-value\">${this.projectCount}</div>\n <div class=\"detail-label\">분석 프로젝트</div>\n </div>\n <div class=\"detail-item\">\n <div class=\"detail-value\">${this.minScore}</div>\n <div class=\"detail-label\">최저</div>\n </div>\n <div class=\"detail-item\">\n <div class=\"detail-value\">${this.maxScore}</div>\n <div class=\"detail-label\">최고</div>\n </div>\n </div>\n </div>\n </div>\n `\n }\n}\n"]}
1
+ {"version":3,"file":"kpi-level1-card.js","sourceRoot":"","sources":["../../../../client/pages/kpi-dashboard/cards/kpi-level1-card.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, css } from 'lit'\nimport { customElement, state, property } from 'lit/decorators.js'\nimport { LitElement } from 'lit'\nimport { client } from '@operato/graphql'\nimport gql from 'graphql-tag'\n\n@customElement('kpi-level1-card')\nexport class KpiLevel1Card extends LitElement {\n static styles = css`\n :host {\n display: block;\n }\n .score-card {\n background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n border-radius: 16px;\n padding: 32px;\n color: white;\n box-shadow: 0 8px 32px rgba(102, 126, 234, 0.3);\n text-align: center;\n position: relative;\n overflow: hidden;\n }\n .score-card::before {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(255, 255, 255, 0.1);\n border-radius: 16px;\n }\n .score-content {\n position: relative;\n z-index: 1;\n }\n .score-title {\n font-size: 1.2rem;\n font-weight: 600;\n margin-bottom: 16px;\n opacity: 0.9;\n }\n .score-value {\n font-size: 4rem;\n font-weight: bold;\n margin-bottom: 8px;\n text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);\n }\n .score-grade {\n font-size: 1.5rem;\n font-weight: 600;\n margin-bottom: 16px;\n opacity: 0.9;\n }\n .score-period {\n font-size: 0.9rem;\n opacity: 0.7;\n margin-bottom: 16px;\n }\n .score-details {\n display: flex;\n justify-content: space-around;\n margin-top: 24px;\n padding-top: 16px;\n border-top: 1px solid rgba(255, 255, 255, 0.2);\n }\n .detail-item {\n text-align: center;\n }\n .detail-value {\n font-size: 1.4rem;\n font-weight: bold;\n margin-bottom: 4px;\n }\n .detail-label {\n font-size: 0.8rem;\n opacity: 0.8;\n }\n .loading {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 200px;\n color: #666;\n font-size: 1.1rem;\n }\n .error {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 200px;\n color: #d32f2f;\n font-size: 1.1rem;\n }\n `\n\n @property({ type: String }) sectorType: string = ''\n @property({ type: String }) buildingUsage: string = ''\n @property({ type: String }) projectScale: string = ''\n @property({ type: String }) facilityType: string = ''\n\n @state() loading = true\n @state() error = ''\n @state() totalScore = 0\n @state() grade = ''\n @state() projectCount = 0\n @state() minScore = 0\n @state() maxScore = 0\n\n connectedCallback() {\n super.connectedCallback()\n this.fetchTotalScore()\n }\n\n override updated(changedProperties: Map<string, unknown>) {\n if (changedProperties.has('sectorType') ||\n changedProperties.has('buildingUsage') ||\n changedProperties.has('projectScale') ||\n changedProperties.has('facilityType')) {\n this.fetchTotalScore()\n }\n }\n\n private calculateGrade(score: number): string {\n if (score >= 90) return 'A+'\n if (score >= 85) return 'A'\n if (score >= 80) return 'A-'\n if (score >= 75) return 'B+'\n if (score >= 70) return 'B'\n if (score >= 65) return 'B-'\n if (score >= 60) return 'C+'\n if (score >= 55) return 'C'\n if (score >= 50) return 'C-'\n return 'D'\n }\n\n async fetchTotalScore() {\n this.loading = true\n this.error = ''\n\n try {\n const response = await client.query({\n query: gql`\n query KpiLevel1Stats($sectorType: String, $buildingUsage: String, $projectScale: String, $facilityType: String) {\n kpiProjectStats(kpiNamePattern: \"Z\", sectorType: $sectorType, buildingUsage: $buildingUsage, projectScale: $projectScale, facilityType: $facilityType) {\n kpiName\n avgVal\n minVal\n maxVal\n projectCount\n }\n }\n `,\n variables: {\n sectorType: this.sectorType || null,\n buildingUsage: this.buildingUsage || null,\n projectScale: this.projectScale || null,\n facilityType: this.facilityType || null\n }\n })\n\n const stats: any[] = response.data.kpiProjectStats || []\n\n if (stats.length === 0) {\n this.error = '데이터가 없습니다.'\n return\n }\n\n const zStat = stats[0]\n this.totalScore = Math.round(zStat.avgVal * 100)\n this.grade = this.calculateGrade(this.totalScore)\n this.projectCount = zStat.projectCount\n this.minScore = Math.round(zStat.minVal * 100)\n this.maxScore = Math.round(zStat.maxVal * 100)\n } catch (e) {\n console.error('총점 데이터를 불러오지 못했습니다:', e)\n this.error = '총점 데이터를 불러오지 못했습니다.'\n } finally {\n this.loading = false\n }\n }\n\n render() {\n if (this.loading) {\n return html`\n <div class=\"score-card\">\n <div class=\"loading\">총점 계산 중...</div>\n </div>\n `\n }\n\n if (this.error) {\n return html`\n <div class=\"score-card\">\n <div class=\"error\">${this.error}</div>\n </div>\n `\n }\n\n return html`\n <div class=\"score-card\">\n <div class=\"score-content\">\n <div class=\"score-title\">그룹 총점</div>\n <div class=\"score-value\">${this.totalScore}</div>\n <div class=\"score-grade\">${this.grade}</div>\n\n <div class=\"score-details\">\n <div class=\"detail-item\">\n <div class=\"detail-value\">${this.projectCount}</div>\n <div class=\"detail-label\">분석 프로젝트</div>\n </div>\n <div class=\"detail-item\">\n <div class=\"detail-value\">${this.minScore}</div>\n <div class=\"detail-label\">최저</div>\n </div>\n <div class=\"detail-item\">\n <div class=\"detail-value\">${this.maxScore}</div>\n <div class=\"detail-label\">최고</div>\n </div>\n </div>\n </div>\n </div>\n `\n }\n}\n"]}
@@ -5,6 +5,8 @@ export declare class KpiLevel2Comparison extends LitElement {
5
5
  static styles: import("lit").CSSResult;
6
6
  sectorType: string;
7
7
  buildingUsage: string;
8
+ projectScale: string;
9
+ facilityType: string;
8
10
  loading: boolean;
9
11
  error: string;
10
12
  radarData: any[];
@@ -11,6 +11,8 @@ let KpiLevel2Comparison = class KpiLevel2Comparison extends LitElement {
11
11
  super(...arguments);
12
12
  this.sectorType = '';
13
13
  this.buildingUsage = '';
14
+ this.projectScale = '';
15
+ this.facilityType = '';
14
16
  this.loading = true;
15
17
  this.error = '';
16
18
  this.radarData = [];
@@ -25,7 +27,10 @@ let KpiLevel2Comparison = class KpiLevel2Comparison extends LitElement {
25
27
  this.fetchCategoryComparison();
26
28
  }
27
29
  updated(changedProperties) {
28
- if (changedProperties.has('sectorType') || changedProperties.has('buildingUsage')) {
30
+ if (changedProperties.has('sectorType') ||
31
+ changedProperties.has('buildingUsage') ||
32
+ changedProperties.has('projectScale') ||
33
+ changedProperties.has('facilityType')) {
29
34
  this.fetchCategoryComparison();
30
35
  }
31
36
  }
@@ -35,8 +40,8 @@ let KpiLevel2Comparison = class KpiLevel2Comparison extends LitElement {
35
40
  try {
36
41
  const response = await client.query({
37
42
  query: gql `
38
- query KpiLevel2Stats($sectorType: String, $buildingUsage: String) {
39
- kpiProjectStats(kpiNamePattern: "X", sectorType: $sectorType, buildingUsage: $buildingUsage) {
43
+ query KpiLevel2Stats($sectorType: String, $buildingUsage: String, $projectScale: String, $facilityType: String) {
44
+ kpiProjectStats(kpiNamePattern: "X", sectorType: $sectorType, buildingUsage: $buildingUsage, projectScale: $projectScale, facilityType: $facilityType) {
40
45
  kpiName
41
46
  categoryName
42
47
  minVal
@@ -52,7 +57,9 @@ let KpiLevel2Comparison = class KpiLevel2Comparison extends LitElement {
52
57
  `,
53
58
  variables: {
54
59
  sectorType: this.sectorType || null,
55
- buildingUsage: this.buildingUsage || null
60
+ buildingUsage: this.buildingUsage || null,
61
+ projectScale: this.projectScale || null,
62
+ facilityType: this.facilityType || null
56
63
  }
57
64
  });
58
65
  const stats = response.data.kpiProjectStats || [];
@@ -307,6 +314,14 @@ __decorate([
307
314
  property({ type: String }),
308
315
  __metadata("design:type", String)
309
316
  ], KpiLevel2Comparison.prototype, "buildingUsage", void 0);
317
+ __decorate([
318
+ property({ type: String }),
319
+ __metadata("design:type", String)
320
+ ], KpiLevel2Comparison.prototype, "projectScale", void 0);
321
+ __decorate([
322
+ property({ type: String }),
323
+ __metadata("design:type", String)
324
+ ], KpiLevel2Comparison.prototype, "facilityType", void 0);
310
325
  __decorate([
311
326
  state(),
312
327
  __metadata("design:type", Object)
@@ -1 +1 @@
1
- {"version":3,"file":"kpi-level2-comparison.js","sourceRoot":"","sources":["../../../../client/pages/kpi-dashboard/cards/kpi-level2-comparison.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, css } from 'lit'\nimport { customElement, state, property } from 'lit/decorators.js'\nimport { LitElement } from 'lit'\nimport { client } from '@operato/graphql'\nimport gql from 'graphql-tag'\nimport '../../components/kpi-radar-chart'\nimport '../../components/kpi-boxplot-chart'\n\n@customElement('kpi-level2-comparison')\nexport class KpiLevel2Comparison extends LitElement {\n static styles = css`\n :host {\n display: block;\n }\n .comparison-container {\n background: #fff;\n border-radius: 16px;\n padding: 24px;\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);\n border: 1px solid #e0e0e0;\n width: 100%;\n }\n .comparison-title {\n font-size: 1.3rem;\n font-weight: bold;\n margin-bottom: 20px;\n color: #333;\n text-align: center;\n }\n .charts-section {\n display: flex;\n gap: 24px;\n margin-bottom: 20px;\n }\n .chart-card {\n flex: 1;\n background: #f8f9fa;\n border-radius: 12px;\n padding: 20px;\n border: 1px solid #e9ecef;\n }\n .chart-title {\n font-size: 1.1rem;\n font-weight: 600;\n margin-bottom: 16px;\n color: #495057;\n text-align: center;\n }\n .chart-container {\n height: 300px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n .loading {\n color: #666;\n font-size: 1rem;\n }\n .error {\n color: #d32f2f;\n font-size: 1rem;\n }\n .no-data {\n color: #666;\n font-size: 1rem;\n text-align: center;\n }\n .summary-info {\n display: flex;\n justify-content: space-around;\n padding: 16px;\n background: #f8f9fa;\n border-radius: 8px;\n margin-top: 16px;\n }\n .summary-item {\n text-align: center;\n }\n .summary-value {\n font-size: 1.2rem;\n font-weight: bold;\n color: #333;\n margin-bottom: 4px;\n }\n .summary-label {\n font-size: 0.9rem;\n color: #666;\n }\n `\n\n @property({ type: String }) sectorType: string = ''\n @property({ type: String }) buildingUsage: string = ''\n\n @state() loading = true\n @state() error = ''\n @state() radarData: any[] = []\n @state() boxplotData: any[] = []\n @state() categories: string[] = []\n @state() totalCategories = 0\n @state() totalKpis = 0\n @state() averageScore = 0\n\n connectedCallback() {\n super.connectedCallback()\n this.fetchCategoryComparison()\n }\n\n override updated(changedProperties: Map<string, unknown>) {\n if (changedProperties.has('sectorType') || changedProperties.has('buildingUsage')) {\n this.fetchCategoryComparison()\n }\n }\n\n async fetchCategoryComparison() {\n this.loading = true\n this.error = ''\n\n try {\n const response = await client.query({\n query: gql`\n query KpiLevel2Stats($sectorType: String, $buildingUsage: String) {\n kpiProjectStats(kpiNamePattern: \"X\", sectorType: $sectorType, buildingUsage: $buildingUsage) {\n kpiName\n categoryName\n minVal\n q1Val\n medVal\n q3Val\n maxVal\n avgVal\n stddevVal\n projectCount\n }\n }\n `,\n variables: {\n sectorType: this.sectorType || null,\n buildingUsage: this.buildingUsage || null\n }\n })\n\n const stats: any[] = response.data.kpiProjectStats || []\n\n if (stats.length === 0) {\n this.error = '데이터가 없습니다.'\n return\n }\n\n // X-level KPIs를 categoryName(Y-level)으로 그룹화\n const categoryMap = new Map<string, any[]>()\n stats.forEach(stat => {\n const catName = stat.categoryName || '기타'\n if (!categoryMap.has(catName)) categoryMap.set(catName, [])\n categoryMap.get(catName)!.push(stat)\n })\n\n this.categories = Array.from(categoryMap.keys())\n this.totalCategories = this.categories.length\n this.totalKpis = stats.length\n\n this.generateRadarData(categoryMap)\n this.generateBoxplotData(categoryMap)\n\n const allAvg = stats.map(s => s.avgVal * 100)\n this.averageScore = Math.round(allAvg.reduce((a, b) => a + b, 0) / allAvg.length)\n } catch (e) {\n console.error('카테고리 비교 데이터를 불러오지 못했습니다:', e)\n this.error = '카테고리 비교 데이터를 불러오지 못했습니다.'\n } finally {\n this.loading = false\n }\n }\n\n private generateRadarData(categoryMap: Map<string, any[]>) {\n const result: any[] = []\n\n this.categories.forEach(category => {\n const stats = categoryMap.get(category)!\n const avgVals = stats.map(s => s.avgVal).filter(v => v > 0)\n const medVals = stats.map(s => s.medVal).filter(v => v > 0)\n const stddevVals = stats.map(s => s.stddevVal).filter(v => v > 0)\n\n if (avgVals.length > 0) {\n result.push({ group: '평균', category, value: avgVals.reduce((a, b) => a + b, 0) / avgVals.length })\n }\n if (medVals.length > 0) {\n result.push({ group: '중앙값', category, value: medVals.reduce((a, b) => a + b, 0) / medVals.length })\n }\n if (stddevVals.length > 0) {\n result.push({ group: '표준편차', category, value: stddevVals.reduce((a, b) => a + b, 0) / stddevVals.length })\n }\n })\n\n this.radarData = result\n }\n\n private generateBoxplotData(categoryMap: Map<string, any[]>) {\n const result: any[] = []\n\n this.categories.forEach(category => {\n const stats = categoryMap.get(category)!\n const avgVals = stats.map(s => s.avgVal)\n const medVals = stats.map(s => s.medVal)\n const minVals = stats.map(s => s.minVal)\n const maxVals = stats.map(s => s.maxVal)\n const q1Vals = stats.map(s => s.q1Val)\n const q3Vals = stats.map(s => s.q3Val)\n\n const avg = (arr: number[]) => arr.reduce((a, b) => a + b, 0) / arr.length\n\n result.push({\n group: category,\n min: Math.min(...minVals),\n q1: avg(q1Vals),\n median: avg(medVals),\n q3: avg(q3Vals),\n max: Math.max(...maxVals),\n mean: avg(avgVals),\n value: avg(avgVals)\n })\n })\n\n this.boxplotData = result\n }\n\n render() {\n if (this.loading) {\n return html`\n <div class=\"comparison-container\">\n <div class=\"comparison-title\">카테고리 비교 분석</div>\n <div class=\"charts-section\">\n <div class=\"chart-card\">\n <div class=\"chart-title\">레이더 차트</div>\n <div class=\"chart-container\">\n <div class=\"loading\">데이터 로딩 중...</div>\n </div>\n </div>\n <div class=\"chart-card\">\n <div class=\"chart-title\">박스플롯</div>\n <div class=\"chart-container\">\n <div class=\"loading\">데이터 로딩 중...</div>\n </div>\n </div>\n </div>\n </div>\n `\n }\n\n if (this.error) {\n return html`\n <div class=\"comparison-container\">\n <div class=\"comparison-title\">카테고리 비교 분석</div>\n <div class=\"charts-section\">\n <div class=\"chart-card\">\n <div class=\"chart-title\">레이더 차트</div>\n <div class=\"chart-container\">\n <div class=\"error\">${this.error}</div>\n </div>\n </div>\n <div class=\"chart-card\">\n <div class=\"chart-title\">박스플롯</div>\n <div class=\"chart-container\">\n <div class=\"error\">${this.error}</div>\n </div>\n </div>\n </div>\n </div>\n `\n }\n\n return html`\n <div class=\"comparison-container\">\n <div class=\"comparison-title\">카테고리 비교 분석</div>\n\n <div class=\"charts-section\">\n <div class=\"chart-card\">\n <div class=\"chart-title\">레이더 차트</div>\n <div class=\"chart-container\">\n ${this.radarData.length > 0\n ? html`<sv-kpi-radar-chart\n .data=${this.radarData}\n .categories=${this.categories}\n .currentGroup=${'평균'}\n ></sv-kpi-radar-chart>`\n : html`<div class=\"no-data\">데이터가 없습니다.</div>`}\n </div>\n </div>\n\n <div class=\"chart-card\">\n <div class=\"chart-title\">박스플롯</div>\n <div class=\"chart-container\">\n ${this.boxplotData.length > 0\n ? html`<sv-kpi-boxplot-chart\n .data=${this.boxplotData}\n .groups=${this.categories}\n .currentGroup=${'평균'}\n ></sv-kpi-boxplot-chart>`\n : html`<div class=\"no-data\">데이터가 없습니다.</div>`}\n </div>\n </div>\n </div>\n\n <div class=\"summary-info\">\n <div class=\"summary-item\">\n <div class=\"summary-value\">${this.totalCategories}</div>\n <div class=\"summary-label\">카테고리</div>\n </div>\n <div class=\"summary-item\">\n <div class=\"summary-value\">${this.totalKpis}</div>\n <div class=\"summary-label\">KPI</div>\n </div>\n <div class=\"summary-item\">\n <div class=\"summary-value\">${this.averageScore}</div>\n <div class=\"summary-label\">평균 점수</div>\n </div>\n </div>\n </div>\n `\n }\n}\n"]}
1
+ {"version":3,"file":"kpi-level2-comparison.js","sourceRoot":"","sources":["../../../../client/pages/kpi-dashboard/cards/kpi-level2-comparison.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, css } from 'lit'\nimport { customElement, state, property } from 'lit/decorators.js'\nimport { LitElement } from 'lit'\nimport { client } from '@operato/graphql'\nimport gql from 'graphql-tag'\nimport '../../components/kpi-radar-chart'\nimport '../../components/kpi-boxplot-chart'\n\n@customElement('kpi-level2-comparison')\nexport class KpiLevel2Comparison extends LitElement {\n static styles = css`\n :host {\n display: block;\n }\n .comparison-container {\n background: #fff;\n border-radius: 16px;\n padding: 24px;\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);\n border: 1px solid #e0e0e0;\n width: 100%;\n }\n .comparison-title {\n font-size: 1.3rem;\n font-weight: bold;\n margin-bottom: 20px;\n color: #333;\n text-align: center;\n }\n .charts-section {\n display: flex;\n gap: 24px;\n margin-bottom: 20px;\n }\n .chart-card {\n flex: 1;\n background: #f8f9fa;\n border-radius: 12px;\n padding: 20px;\n border: 1px solid #e9ecef;\n }\n .chart-title {\n font-size: 1.1rem;\n font-weight: 600;\n margin-bottom: 16px;\n color: #495057;\n text-align: center;\n }\n .chart-container {\n height: 300px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n .loading {\n color: #666;\n font-size: 1rem;\n }\n .error {\n color: #d32f2f;\n font-size: 1rem;\n }\n .no-data {\n color: #666;\n font-size: 1rem;\n text-align: center;\n }\n .summary-info {\n display: flex;\n justify-content: space-around;\n padding: 16px;\n background: #f8f9fa;\n border-radius: 8px;\n margin-top: 16px;\n }\n .summary-item {\n text-align: center;\n }\n .summary-value {\n font-size: 1.2rem;\n font-weight: bold;\n color: #333;\n margin-bottom: 4px;\n }\n .summary-label {\n font-size: 0.9rem;\n color: #666;\n }\n `\n\n @property({ type: String }) sectorType: string = ''\n @property({ type: String }) buildingUsage: string = ''\n @property({ type: String }) projectScale: string = ''\n @property({ type: String }) facilityType: string = ''\n\n @state() loading = true\n @state() error = ''\n @state() radarData: any[] = []\n @state() boxplotData: any[] = []\n @state() categories: string[] = []\n @state() totalCategories = 0\n @state() totalKpis = 0\n @state() averageScore = 0\n\n connectedCallback() {\n super.connectedCallback()\n this.fetchCategoryComparison()\n }\n\n override updated(changedProperties: Map<string, unknown>) {\n if (changedProperties.has('sectorType') ||\n changedProperties.has('buildingUsage') ||\n changedProperties.has('projectScale') ||\n changedProperties.has('facilityType')) {\n this.fetchCategoryComparison()\n }\n }\n\n async fetchCategoryComparison() {\n this.loading = true\n this.error = ''\n\n try {\n const response = await client.query({\n query: gql`\n query KpiLevel2Stats($sectorType: String, $buildingUsage: String, $projectScale: String, $facilityType: String) {\n kpiProjectStats(kpiNamePattern: \"X\", sectorType: $sectorType, buildingUsage: $buildingUsage, projectScale: $projectScale, facilityType: $facilityType) {\n kpiName\n categoryName\n minVal\n q1Val\n medVal\n q3Val\n maxVal\n avgVal\n stddevVal\n projectCount\n }\n }\n `,\n variables: {\n sectorType: this.sectorType || null,\n buildingUsage: this.buildingUsage || null,\n projectScale: this.projectScale || null,\n facilityType: this.facilityType || null\n }\n })\n\n const stats: any[] = response.data.kpiProjectStats || []\n\n if (stats.length === 0) {\n this.error = '데이터가 없습니다.'\n return\n }\n\n // X-level KPIs를 categoryName(Y-level)으로 그룹화\n const categoryMap = new Map<string, any[]>()\n stats.forEach(stat => {\n const catName = stat.categoryName || '기타'\n if (!categoryMap.has(catName)) categoryMap.set(catName, [])\n categoryMap.get(catName)!.push(stat)\n })\n\n this.categories = Array.from(categoryMap.keys())\n this.totalCategories = this.categories.length\n this.totalKpis = stats.length\n\n this.generateRadarData(categoryMap)\n this.generateBoxplotData(categoryMap)\n\n const allAvg = stats.map(s => s.avgVal * 100)\n this.averageScore = Math.round(allAvg.reduce((a, b) => a + b, 0) / allAvg.length)\n } catch (e) {\n console.error('카테고리 비교 데이터를 불러오지 못했습니다:', e)\n this.error = '카테고리 비교 데이터를 불러오지 못했습니다.'\n } finally {\n this.loading = false\n }\n }\n\n private generateRadarData(categoryMap: Map<string, any[]>) {\n const result: any[] = []\n\n this.categories.forEach(category => {\n const stats = categoryMap.get(category)!\n const avgVals = stats.map(s => s.avgVal).filter(v => v > 0)\n const medVals = stats.map(s => s.medVal).filter(v => v > 0)\n const stddevVals = stats.map(s => s.stddevVal).filter(v => v > 0)\n\n if (avgVals.length > 0) {\n result.push({ group: '평균', category, value: avgVals.reduce((a, b) => a + b, 0) / avgVals.length })\n }\n if (medVals.length > 0) {\n result.push({ group: '중앙값', category, value: medVals.reduce((a, b) => a + b, 0) / medVals.length })\n }\n if (stddevVals.length > 0) {\n result.push({ group: '표준편차', category, value: stddevVals.reduce((a, b) => a + b, 0) / stddevVals.length })\n }\n })\n\n this.radarData = result\n }\n\n private generateBoxplotData(categoryMap: Map<string, any[]>) {\n const result: any[] = []\n\n this.categories.forEach(category => {\n const stats = categoryMap.get(category)!\n const avgVals = stats.map(s => s.avgVal)\n const medVals = stats.map(s => s.medVal)\n const minVals = stats.map(s => s.minVal)\n const maxVals = stats.map(s => s.maxVal)\n const q1Vals = stats.map(s => s.q1Val)\n const q3Vals = stats.map(s => s.q3Val)\n\n const avg = (arr: number[]) => arr.reduce((a, b) => a + b, 0) / arr.length\n\n result.push({\n group: category,\n min: Math.min(...minVals),\n q1: avg(q1Vals),\n median: avg(medVals),\n q3: avg(q3Vals),\n max: Math.max(...maxVals),\n mean: avg(avgVals),\n value: avg(avgVals)\n })\n })\n\n this.boxplotData = result\n }\n\n render() {\n if (this.loading) {\n return html`\n <div class=\"comparison-container\">\n <div class=\"comparison-title\">카테고리 비교 분석</div>\n <div class=\"charts-section\">\n <div class=\"chart-card\">\n <div class=\"chart-title\">레이더 차트</div>\n <div class=\"chart-container\">\n <div class=\"loading\">데이터 로딩 중...</div>\n </div>\n </div>\n <div class=\"chart-card\">\n <div class=\"chart-title\">박스플롯</div>\n <div class=\"chart-container\">\n <div class=\"loading\">데이터 로딩 중...</div>\n </div>\n </div>\n </div>\n </div>\n `\n }\n\n if (this.error) {\n return html`\n <div class=\"comparison-container\">\n <div class=\"comparison-title\">카테고리 비교 분석</div>\n <div class=\"charts-section\">\n <div class=\"chart-card\">\n <div class=\"chart-title\">레이더 차트</div>\n <div class=\"chart-container\">\n <div class=\"error\">${this.error}</div>\n </div>\n </div>\n <div class=\"chart-card\">\n <div class=\"chart-title\">박스플롯</div>\n <div class=\"chart-container\">\n <div class=\"error\">${this.error}</div>\n </div>\n </div>\n </div>\n </div>\n `\n }\n\n return html`\n <div class=\"comparison-container\">\n <div class=\"comparison-title\">카테고리 비교 분석</div>\n\n <div class=\"charts-section\">\n <div class=\"chart-card\">\n <div class=\"chart-title\">레이더 차트</div>\n <div class=\"chart-container\">\n ${this.radarData.length > 0\n ? html`<sv-kpi-radar-chart\n .data=${this.radarData}\n .categories=${this.categories}\n .currentGroup=${'평균'}\n ></sv-kpi-radar-chart>`\n : html`<div class=\"no-data\">데이터가 없습니다.</div>`}\n </div>\n </div>\n\n <div class=\"chart-card\">\n <div class=\"chart-title\">박스플롯</div>\n <div class=\"chart-container\">\n ${this.boxplotData.length > 0\n ? html`<sv-kpi-boxplot-chart\n .data=${this.boxplotData}\n .groups=${this.categories}\n .currentGroup=${'평균'}\n ></sv-kpi-boxplot-chart>`\n : html`<div class=\"no-data\">데이터가 없습니다.</div>`}\n </div>\n </div>\n </div>\n\n <div class=\"summary-info\">\n <div class=\"summary-item\">\n <div class=\"summary-value\">${this.totalCategories}</div>\n <div class=\"summary-label\">카테고리</div>\n </div>\n <div class=\"summary-item\">\n <div class=\"summary-value\">${this.totalKpis}</div>\n <div class=\"summary-label\">KPI</div>\n </div>\n <div class=\"summary-item\">\n <div class=\"summary-value\">${this.averageScore}</div>\n <div class=\"summary-label\">평균 점수</div>\n </div>\n </div>\n </div>\n `\n }\n}\n"]}
@@ -5,6 +5,8 @@ export declare class KpiLevel3Comparison extends LitElement {
5
5
  static styles: import("lit").CSSResult;
6
6
  sectorType: string;
7
7
  buildingUsage: string;
8
+ projectScale: string;
9
+ facilityType: string;
8
10
  loading: boolean;
9
11
  error: string;
10
12
  radarData: any[];
@@ -11,6 +11,8 @@ let KpiLevel3Comparison = class KpiLevel3Comparison extends LitElement {
11
11
  super(...arguments);
12
12
  this.sectorType = '';
13
13
  this.buildingUsage = '';
14
+ this.projectScale = '';
15
+ this.facilityType = '';
14
16
  this.loading = true;
15
17
  this.error = '';
16
18
  this.radarData = [];
@@ -27,7 +29,10 @@ let KpiLevel3Comparison = class KpiLevel3Comparison extends LitElement {
27
29
  this.fetchKpiComparison();
28
30
  }
29
31
  updated(changedProperties) {
30
- if (changedProperties.has('sectorType') || changedProperties.has('buildingUsage')) {
32
+ if (changedProperties.has('sectorType') ||
33
+ changedProperties.has('buildingUsage') ||
34
+ changedProperties.has('projectScale') ||
35
+ changedProperties.has('facilityType')) {
31
36
  this.fetchKpiComparison();
32
37
  }
33
38
  }
@@ -38,8 +43,8 @@ let KpiLevel3Comparison = class KpiLevel3Comparison extends LitElement {
38
43
  try {
39
44
  const response = await client.query({
40
45
  query: gql `
41
- query KpiLevel3Stats($sectorType: String, $buildingUsage: String) {
42
- kpiProjectStats(kpiNamePattern: "X", sectorType: $sectorType, buildingUsage: $buildingUsage) {
46
+ query KpiLevel3Stats($sectorType: String, $buildingUsage: String, $projectScale: String, $facilityType: String) {
47
+ kpiProjectStats(kpiNamePattern: "X", sectorType: $sectorType, buildingUsage: $buildingUsage, projectScale: $projectScale, facilityType: $facilityType) {
43
48
  kpiName
44
49
  categoryName
45
50
  minVal
@@ -55,7 +60,9 @@ let KpiLevel3Comparison = class KpiLevel3Comparison extends LitElement {
55
60
  `,
56
61
  variables: {
57
62
  sectorType: this.sectorType || null,
58
- buildingUsage: this.buildingUsage || null
63
+ buildingUsage: this.buildingUsage || null,
64
+ projectScale: this.projectScale || null,
65
+ facilityType: this.facilityType || null
59
66
  }
60
67
  });
61
68
  const stats = response.data.kpiProjectStats || [];
@@ -386,6 +393,14 @@ __decorate([
386
393
  property({ type: String }),
387
394
  __metadata("design:type", String)
388
395
  ], KpiLevel3Comparison.prototype, "buildingUsage", void 0);
396
+ __decorate([
397
+ property({ type: String }),
398
+ __metadata("design:type", String)
399
+ ], KpiLevel3Comparison.prototype, "projectScale", void 0);
400
+ __decorate([
401
+ property({ type: String }),
402
+ __metadata("design:type", String)
403
+ ], KpiLevel3Comparison.prototype, "facilityType", void 0);
389
404
  __decorate([
390
405
  state(),
391
406
  __metadata("design:type", Object)
@@ -1 +1 @@
1
- {"version":3,"file":"kpi-level3-comparison.js","sourceRoot":"","sources":["../../../../client/pages/kpi-dashboard/cards/kpi-level3-comparison.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, css, nothing } from 'lit'\nimport { customElement, state, property } from 'lit/decorators.js'\nimport { LitElement } from 'lit'\nimport { client } from '@operato/graphql'\nimport gql from 'graphql-tag'\nimport '../../components/kpi-radar-chart'\nimport '../../components/kpi-boxplot-chart'\n\n@customElement('kpi-level3-comparison')\nexport class KpiLevel3Comparison extends LitElement {\n static styles = css`\n :host {\n display: block;\n }\n .comparison-container {\n background: #fff;\n border-radius: 16px;\n padding: 24px;\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);\n border: 1px solid #e0e0e0;\n width: 100%;\n }\n .comparison-title {\n font-size: 1.3rem;\n font-weight: bold;\n margin-bottom: 20px;\n color: #333;\n text-align: center;\n }\n .category-selector {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-bottom: 24px;\n padding: 16px;\n background: #f8f9fa;\n border-radius: 8px;\n border: 1px solid #e9ecef;\n }\n .selector-label {\n font-weight: 600;\n color: #495057;\n min-width: 80px;\n }\n .category-select {\n padding: 8px 12px;\n border: 1px solid #ced4da;\n border-radius: 6px;\n background: white;\n font-size: 1rem;\n min-width: 200px;\n }\n .category-select:focus {\n outline: none;\n border-color: #667eea;\n box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.2);\n }\n .charts-section {\n display: flex;\n gap: 24px;\n margin-bottom: 20px;\n }\n .chart-card {\n flex: 1;\n background: #f8f9fa;\n border-radius: 12px;\n padding: 20px;\n border: 1px solid #e9ecef;\n }\n .chart-title {\n font-size: 1.1rem;\n font-weight: 600;\n margin-bottom: 16px;\n color: #495057;\n text-align: center;\n }\n .chart-container {\n height: 300px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n .loading {\n color: #666;\n font-size: 1rem;\n }\n .error {\n color: #d32f2f;\n font-size: 1rem;\n }\n .no-data {\n color: #666;\n font-size: 1rem;\n text-align: center;\n }\n .no-category {\n color: #666;\n font-size: 1rem;\n text-align: center;\n font-style: italic;\n }\n .summary-info {\n display: flex;\n justify-content: space-around;\n padding: 16px;\n background: #f8f9fa;\n border-radius: 8px;\n margin-top: 16px;\n }\n .summary-item {\n text-align: center;\n }\n .summary-value {\n font-size: 1.2rem;\n font-weight: bold;\n color: #333;\n margin-bottom: 4px;\n }\n .summary-label {\n font-size: 0.9rem;\n color: #666;\n }\n `\n\n @property({ type: String }) sectorType: string = ''\n @property({ type: String }) buildingUsage: string = ''\n\n @state() loading = true\n @state() error = ''\n @state() radarData: any[] = []\n @state() boxplotData: any[] = []\n @state() kpis: string[] = []\n @state() availableCategories: string[] = []\n @state() selectedCategory = ''\n @state() totalKpis = 0\n @state() averageScore = 0\n @state() categoryStats = new Map<string, any[]>()\n\n connectedCallback() {\n super.connectedCallback()\n this.fetchKpiComparison()\n }\n\n override updated(changedProperties: Map<string, unknown>) {\n if (changedProperties.has('sectorType') || changedProperties.has('buildingUsage')) {\n this.fetchKpiComparison()\n }\n }\n\n async fetchKpiComparison() {\n this.loading = true\n this.error = ''\n\n try {\n const response = await client.query({\n query: gql`\n query KpiLevel3Stats($sectorType: String, $buildingUsage: String) {\n kpiProjectStats(kpiNamePattern: \"X\", sectorType: $sectorType, buildingUsage: $buildingUsage) {\n kpiName\n categoryName\n minVal\n q1Val\n medVal\n q3Val\n maxVal\n avgVal\n stddevVal\n projectCount\n }\n }\n `,\n variables: {\n sectorType: this.sectorType || null,\n buildingUsage: this.buildingUsage || null\n }\n })\n\n const stats: any[] = response.data.kpiProjectStats || []\n\n if (stats.length === 0) {\n this.error = '데이터가 없습니다.'\n return\n }\n\n // X-level KPIs를 categoryName(Y-level)으로 그룹화\n const categoryMap = new Map<string, any[]>()\n stats.forEach(stat => {\n const catName = stat.categoryName || '기타'\n if (!categoryMap.has(catName)) categoryMap.set(catName, [])\n categoryMap.get(catName)!.push(stat)\n })\n\n this.categoryStats = categoryMap\n this.availableCategories = Array.from(categoryMap.keys())\n\n // 카테고리 변경 시 선택 초기화\n if (!this.selectedCategory || !categoryMap.has(this.selectedCategory)) {\n this.selectedCategory = this.availableCategories[0] ?? ''\n }\n\n if (this.selectedCategory) {\n this.generateKpiCharts()\n }\n } catch (e) {\n console.error('KPI 비교 데이터를 불러오지 못했습니다:', e)\n this.error = 'KPI 비교 데이터를 불러오지 못했습니다.'\n } finally {\n this.loading = false\n }\n }\n\n private onCategoryChange(event: Event) {\n const target = event.target as HTMLSelectElement\n this.selectedCategory = target.value\n this.generateKpiCharts()\n }\n\n private generateKpiCharts() {\n if (!this.selectedCategory || !this.categoryStats.has(this.selectedCategory)) {\n this.radarData = []\n this.boxplotData = []\n this.kpis = []\n this.totalKpis = 0\n this.averageScore = 0\n return\n }\n\n const categoryData = this.categoryStats.get(this.selectedCategory)!\n this.kpis = categoryData.map(s => s.kpiName)\n this.totalKpis = this.kpis.length\n\n this.generateRadarData(categoryData)\n this.generateBoxplotData(categoryData)\n\n const scores = categoryData.map(s => s.avgVal * 100)\n this.averageScore = Math.round(scores.reduce((a, b) => a + b, 0) / scores.length)\n }\n\n private generateRadarData(categoryData: any[]) {\n const result: any[] = []\n\n this.kpis.forEach(kpiName => {\n const stat = categoryData.find(s => s.kpiName === kpiName)\n if (stat) {\n if (stat.avgVal > 0) result.push({ group: '평균', category: kpiName, value: stat.avgVal })\n if (stat.medVal > 0) result.push({ group: '중앙값', category: kpiName, value: stat.medVal })\n if (stat.stddevVal > 0) result.push({ group: '표준편차', category: kpiName, value: stat.stddevVal })\n }\n })\n\n this.radarData = result\n }\n\n private generateBoxplotData(categoryData: any[]) {\n const result: any[] = []\n\n this.kpis.forEach(kpiName => {\n const stat = categoryData.find(s => s.kpiName === kpiName)\n if (stat && stat.avgVal > 0) {\n result.push({\n group: kpiName,\n min: stat.minVal,\n q1: stat.q1Val,\n median: stat.medVal,\n q3: stat.q3Val,\n max: stat.maxVal,\n mean: stat.avgVal,\n value: stat.avgVal\n })\n }\n })\n\n this.boxplotData = result\n }\n\n render() {\n if (this.loading) {\n return html`\n <div class=\"comparison-container\">\n <div class=\"comparison-title\">KPI 상세 비교 분석</div>\n <div class=\"category-selector\">\n <div class=\"selector-label\">카테고리:</div>\n <select class=\"category-select\" disabled>\n <option>로딩 중...</option>\n </select>\n </div>\n <div class=\"charts-section\">\n <div class=\"chart-card\">\n <div class=\"chart-title\">레이더 차트</div>\n <div class=\"chart-container\">\n <div class=\"loading\">데이터 로딩 중...</div>\n </div>\n </div>\n <div class=\"chart-card\">\n <div class=\"chart-title\">박스플롯</div>\n <div class=\"chart-container\">\n <div class=\"loading\">데이터 로딩 중...</div>\n </div>\n </div>\n </div>\n </div>\n `\n }\n\n if (this.error) {\n return html`\n <div class=\"comparison-container\">\n <div class=\"comparison-title\">KPI 상세 비교 분석</div>\n <div class=\"category-selector\">\n <div class=\"selector-label\">카테고리:</div>\n <select class=\"category-select\" disabled>\n <option>오류 발생</option>\n </select>\n </div>\n <div class=\"charts-section\">\n <div class=\"chart-card\">\n <div class=\"chart-title\">레이더 차트</div>\n <div class=\"chart-container\">\n <div class=\"error\">${this.error}</div>\n </div>\n </div>\n <div class=\"chart-card\">\n <div class=\"chart-title\">박스플롯</div>\n <div class=\"chart-container\">\n <div class=\"error\">${this.error}</div>\n </div>\n </div>\n </div>\n </div>\n `\n }\n\n return html`\n <div class=\"comparison-container\">\n <div class=\"comparison-title\">KPI 상세 비교 분석</div>\n\n <div class=\"category-selector\">\n <div class=\"selector-label\">카테고리:</div>\n <select class=\"category-select\" .value=${this.selectedCategory} @change=${this.onCategoryChange}>\n ${this.availableCategories.map(category => html`<option value=\"${category}\">${category}</option>`)}\n </select>\n </div>\n\n <div class=\"charts-section\">\n <div class=\"chart-card\">\n <div class=\"chart-title\">레이더 차트</div>\n <div class=\"chart-container\">\n ${this.selectedCategory\n ? this.radarData.length > 0\n ? html`<sv-kpi-radar-chart\n .data=${this.radarData}\n .categories=${this.kpis}\n .currentGroup=${'평균'}\n ></sv-kpi-radar-chart>`\n : html`<div class=\"no-data\">선택된 카테고리에 데이터가 없습니다.</div>`\n : html`<div class=\"no-category\">카테고리를 선택해주세요.</div>`}\n </div>\n </div>\n\n <div class=\"chart-card\">\n <div class=\"chart-title\">박스플롯</div>\n <div class=\"chart-container\">\n ${this.selectedCategory\n ? this.boxplotData.length > 0\n ? html`<sv-kpi-boxplot-chart\n .data=${this.boxplotData}\n .groups=${this.kpis}\n .currentGroup=${'평균'}\n ></sv-kpi-boxplot-chart>`\n : html`<div class=\"no-data\">선택된 카테고리에 데이터가 없습니다.</div>`\n : html`<div class=\"no-category\">카테고리를 선택해주세요.</div>`}\n </div>\n </div>\n </div>\n\n ${this.selectedCategory\n ? html`\n <div class=\"summary-info\">\n <div class=\"summary-item\">\n <div class=\"summary-value\">${this.selectedCategory}</div>\n <div class=\"summary-label\">선택된 카테고리</div>\n </div>\n <div class=\"summary-item\">\n <div class=\"summary-value\">${this.totalKpis}</div>\n <div class=\"summary-label\">KPI</div>\n </div>\n <div class=\"summary-item\">\n <div class=\"summary-value\">${this.averageScore}</div>\n <div class=\"summary-label\">평균 점수</div>\n </div>\n </div>\n `\n : nothing}\n </div>\n `\n }\n}\n"]}
1
+ {"version":3,"file":"kpi-level3-comparison.js","sourceRoot":"","sources":["../../../../client/pages/kpi-dashboard/cards/kpi-level3-comparison.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, css, nothing } from 'lit'\nimport { customElement, state, property } from 'lit/decorators.js'\nimport { LitElement } from 'lit'\nimport { client } from '@operato/graphql'\nimport gql from 'graphql-tag'\nimport '../../components/kpi-radar-chart'\nimport '../../components/kpi-boxplot-chart'\n\n@customElement('kpi-level3-comparison')\nexport class KpiLevel3Comparison extends LitElement {\n static styles = css`\n :host {\n display: block;\n }\n .comparison-container {\n background: #fff;\n border-radius: 16px;\n padding: 24px;\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);\n border: 1px solid #e0e0e0;\n width: 100%;\n }\n .comparison-title {\n font-size: 1.3rem;\n font-weight: bold;\n margin-bottom: 20px;\n color: #333;\n text-align: center;\n }\n .category-selector {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-bottom: 24px;\n padding: 16px;\n background: #f8f9fa;\n border-radius: 8px;\n border: 1px solid #e9ecef;\n }\n .selector-label {\n font-weight: 600;\n color: #495057;\n min-width: 80px;\n }\n .category-select {\n padding: 8px 12px;\n border: 1px solid #ced4da;\n border-radius: 6px;\n background: white;\n font-size: 1rem;\n min-width: 200px;\n }\n .category-select:focus {\n outline: none;\n border-color: #667eea;\n box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.2);\n }\n .charts-section {\n display: flex;\n gap: 24px;\n margin-bottom: 20px;\n }\n .chart-card {\n flex: 1;\n background: #f8f9fa;\n border-radius: 12px;\n padding: 20px;\n border: 1px solid #e9ecef;\n }\n .chart-title {\n font-size: 1.1rem;\n font-weight: 600;\n margin-bottom: 16px;\n color: #495057;\n text-align: center;\n }\n .chart-container {\n height: 300px;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n .loading {\n color: #666;\n font-size: 1rem;\n }\n .error {\n color: #d32f2f;\n font-size: 1rem;\n }\n .no-data {\n color: #666;\n font-size: 1rem;\n text-align: center;\n }\n .no-category {\n color: #666;\n font-size: 1rem;\n text-align: center;\n font-style: italic;\n }\n .summary-info {\n display: flex;\n justify-content: space-around;\n padding: 16px;\n background: #f8f9fa;\n border-radius: 8px;\n margin-top: 16px;\n }\n .summary-item {\n text-align: center;\n }\n .summary-value {\n font-size: 1.2rem;\n font-weight: bold;\n color: #333;\n margin-bottom: 4px;\n }\n .summary-label {\n font-size: 0.9rem;\n color: #666;\n }\n `\n\n @property({ type: String }) sectorType: string = ''\n @property({ type: String }) buildingUsage: string = ''\n @property({ type: String }) projectScale: string = ''\n @property({ type: String }) facilityType: string = ''\n\n @state() loading = true\n @state() error = ''\n @state() radarData: any[] = []\n @state() boxplotData: any[] = []\n @state() kpis: string[] = []\n @state() availableCategories: string[] = []\n @state() selectedCategory = ''\n @state() totalKpis = 0\n @state() averageScore = 0\n @state() categoryStats = new Map<string, any[]>()\n\n connectedCallback() {\n super.connectedCallback()\n this.fetchKpiComparison()\n }\n\n override updated(changedProperties: Map<string, unknown>) {\n if (changedProperties.has('sectorType') ||\n changedProperties.has('buildingUsage') ||\n changedProperties.has('projectScale') ||\n changedProperties.has('facilityType')) {\n this.fetchKpiComparison()\n }\n }\n\n async fetchKpiComparison() {\n this.loading = true\n this.error = ''\n\n try {\n const response = await client.query({\n query: gql`\n query KpiLevel3Stats($sectorType: String, $buildingUsage: String, $projectScale: String, $facilityType: String) {\n kpiProjectStats(kpiNamePattern: \"X\", sectorType: $sectorType, buildingUsage: $buildingUsage, projectScale: $projectScale, facilityType: $facilityType) {\n kpiName\n categoryName\n minVal\n q1Val\n medVal\n q3Val\n maxVal\n avgVal\n stddevVal\n projectCount\n }\n }\n `,\n variables: {\n sectorType: this.sectorType || null,\n buildingUsage: this.buildingUsage || null,\n projectScale: this.projectScale || null,\n facilityType: this.facilityType || null\n }\n })\n\n const stats: any[] = response.data.kpiProjectStats || []\n\n if (stats.length === 0) {\n this.error = '데이터가 없습니다.'\n return\n }\n\n // X-level KPIs를 categoryName(Y-level)으로 그룹화\n const categoryMap = new Map<string, any[]>()\n stats.forEach(stat => {\n const catName = stat.categoryName || '기타'\n if (!categoryMap.has(catName)) categoryMap.set(catName, [])\n categoryMap.get(catName)!.push(stat)\n })\n\n this.categoryStats = categoryMap\n this.availableCategories = Array.from(categoryMap.keys())\n\n // 카테고리 변경 시 선택 초기화\n if (!this.selectedCategory || !categoryMap.has(this.selectedCategory)) {\n this.selectedCategory = this.availableCategories[0] ?? ''\n }\n\n if (this.selectedCategory) {\n this.generateKpiCharts()\n }\n } catch (e) {\n console.error('KPI 비교 데이터를 불러오지 못했습니다:', e)\n this.error = 'KPI 비교 데이터를 불러오지 못했습니다.'\n } finally {\n this.loading = false\n }\n }\n\n private onCategoryChange(event: Event) {\n const target = event.target as HTMLSelectElement\n this.selectedCategory = target.value\n this.generateKpiCharts()\n }\n\n private generateKpiCharts() {\n if (!this.selectedCategory || !this.categoryStats.has(this.selectedCategory)) {\n this.radarData = []\n this.boxplotData = []\n this.kpis = []\n this.totalKpis = 0\n this.averageScore = 0\n return\n }\n\n const categoryData = this.categoryStats.get(this.selectedCategory)!\n this.kpis = categoryData.map(s => s.kpiName)\n this.totalKpis = this.kpis.length\n\n this.generateRadarData(categoryData)\n this.generateBoxplotData(categoryData)\n\n const scores = categoryData.map(s => s.avgVal * 100)\n this.averageScore = Math.round(scores.reduce((a, b) => a + b, 0) / scores.length)\n }\n\n private generateRadarData(categoryData: any[]) {\n const result: any[] = []\n\n this.kpis.forEach(kpiName => {\n const stat = categoryData.find(s => s.kpiName === kpiName)\n if (stat) {\n if (stat.avgVal > 0) result.push({ group: '평균', category: kpiName, value: stat.avgVal })\n if (stat.medVal > 0) result.push({ group: '중앙값', category: kpiName, value: stat.medVal })\n if (stat.stddevVal > 0) result.push({ group: '표준편차', category: kpiName, value: stat.stddevVal })\n }\n })\n\n this.radarData = result\n }\n\n private generateBoxplotData(categoryData: any[]) {\n const result: any[] = []\n\n this.kpis.forEach(kpiName => {\n const stat = categoryData.find(s => s.kpiName === kpiName)\n if (stat && stat.avgVal > 0) {\n result.push({\n group: kpiName,\n min: stat.minVal,\n q1: stat.q1Val,\n median: stat.medVal,\n q3: stat.q3Val,\n max: stat.maxVal,\n mean: stat.avgVal,\n value: stat.avgVal\n })\n }\n })\n\n this.boxplotData = result\n }\n\n render() {\n if (this.loading) {\n return html`\n <div class=\"comparison-container\">\n <div class=\"comparison-title\">KPI 상세 비교 분석</div>\n <div class=\"category-selector\">\n <div class=\"selector-label\">카테고리:</div>\n <select class=\"category-select\" disabled>\n <option>로딩 중...</option>\n </select>\n </div>\n <div class=\"charts-section\">\n <div class=\"chart-card\">\n <div class=\"chart-title\">레이더 차트</div>\n <div class=\"chart-container\">\n <div class=\"loading\">데이터 로딩 중...</div>\n </div>\n </div>\n <div class=\"chart-card\">\n <div class=\"chart-title\">박스플롯</div>\n <div class=\"chart-container\">\n <div class=\"loading\">데이터 로딩 중...</div>\n </div>\n </div>\n </div>\n </div>\n `\n }\n\n if (this.error) {\n return html`\n <div class=\"comparison-container\">\n <div class=\"comparison-title\">KPI 상세 비교 분석</div>\n <div class=\"category-selector\">\n <div class=\"selector-label\">카테고리:</div>\n <select class=\"category-select\" disabled>\n <option>오류 발생</option>\n </select>\n </div>\n <div class=\"charts-section\">\n <div class=\"chart-card\">\n <div class=\"chart-title\">레이더 차트</div>\n <div class=\"chart-container\">\n <div class=\"error\">${this.error}</div>\n </div>\n </div>\n <div class=\"chart-card\">\n <div class=\"chart-title\">박스플롯</div>\n <div class=\"chart-container\">\n <div class=\"error\">${this.error}</div>\n </div>\n </div>\n </div>\n </div>\n `\n }\n\n return html`\n <div class=\"comparison-container\">\n <div class=\"comparison-title\">KPI 상세 비교 분석</div>\n\n <div class=\"category-selector\">\n <div class=\"selector-label\">카테고리:</div>\n <select class=\"category-select\" .value=${this.selectedCategory} @change=${this.onCategoryChange}>\n ${this.availableCategories.map(category => html`<option value=\"${category}\">${category}</option>`)}\n </select>\n </div>\n\n <div class=\"charts-section\">\n <div class=\"chart-card\">\n <div class=\"chart-title\">레이더 차트</div>\n <div class=\"chart-container\">\n ${this.selectedCategory\n ? this.radarData.length > 0\n ? html`<sv-kpi-radar-chart\n .data=${this.radarData}\n .categories=${this.kpis}\n .currentGroup=${'평균'}\n ></sv-kpi-radar-chart>`\n : html`<div class=\"no-data\">선택된 카테고리에 데이터가 없습니다.</div>`\n : html`<div class=\"no-category\">카테고리를 선택해주세요.</div>`}\n </div>\n </div>\n\n <div class=\"chart-card\">\n <div class=\"chart-title\">박스플롯</div>\n <div class=\"chart-container\">\n ${this.selectedCategory\n ? this.boxplotData.length > 0\n ? html`<sv-kpi-boxplot-chart\n .data=${this.boxplotData}\n .groups=${this.kpis}\n .currentGroup=${'평균'}\n ></sv-kpi-boxplot-chart>`\n : html`<div class=\"no-data\">선택된 카테고리에 데이터가 없습니다.</div>`\n : html`<div class=\"no-category\">카테고리를 선택해주세요.</div>`}\n </div>\n </div>\n </div>\n\n ${this.selectedCategory\n ? html`\n <div class=\"summary-info\">\n <div class=\"summary-item\">\n <div class=\"summary-value\">${this.selectedCategory}</div>\n <div class=\"summary-label\">선택된 카테고리</div>\n </div>\n <div class=\"summary-item\">\n <div class=\"summary-value\">${this.totalKpis}</div>\n <div class=\"summary-label\">KPI</div>\n </div>\n <div class=\"summary-item\">\n <div class=\"summary-value\">${this.averageScore}</div>\n <div class=\"summary-label\">평균 점수</div>\n </div>\n </div>\n `\n : nothing}\n </div>\n `\n }\n}\n"]}
@@ -11,6 +11,8 @@ export declare class KpiLeftPanel extends LitElement {
11
11
  endYearMonth: string;
12
12
  sectorType: string;
13
13
  buildingUsage: string;
14
+ projectScale: string;
15
+ facilityType: string;
14
16
  private isAllPeriod;
15
17
  private chartData;
16
18
  private chartCategories;
@@ -7,6 +7,7 @@ import { client } from '@operato/graphql';
7
7
  import '../../../components/kpi-radar-chart.js';
8
8
  import '../../../components/kpi-boxplot-chart.js';
9
9
  import '../../../components/kpi-mini-trend-chart.js';
10
+ import { PROJECT_SCALE_OPTIONS, FACILITY_TYPE_OPTIONS } from '../../../shared/project-axis';
10
11
  let KpiLeftPanel = class KpiLeftPanel extends LitElement {
11
12
  constructor() {
12
13
  super(...arguments);
@@ -17,6 +18,8 @@ let KpiLeftPanel = class KpiLeftPanel extends LitElement {
17
18
  this.endYearMonth = ''; // YYYY-MM 형식
18
19
  this.sectorType = '';
19
20
  this.buildingUsage = '';
21
+ this.projectScale = '';
22
+ this.facilityType = '';
20
23
  this.isAllPeriod = false;
21
24
  this.chartData = [];
22
25
  this.chartCategories = [];
@@ -66,7 +69,9 @@ let KpiLeftPanel = class KpiLeftPanel extends LitElement {
66
69
  changedProperties.has('startYearMonth') ||
67
70
  changedProperties.has('endYearMonth') ||
68
71
  changedProperties.has('sectorType') ||
69
- changedProperties.has('buildingUsage')) {
72
+ changedProperties.has('buildingUsage') ||
73
+ changedProperties.has('projectScale') ||
74
+ changedProperties.has('facilityType')) {
70
75
  // 기간 정보가 설정되어 있을 때만 조회 (빈 문자열이면 전체 기간)
71
76
  if (this.startYearMonth !== undefined && this.endYearMonth !== undefined) {
72
77
  this.fetchKpiYComprehensiveStats();
@@ -84,12 +89,16 @@ let KpiLeftPanel = class KpiLeftPanel extends LitElement {
84
89
  $endYearMonth: String
85
90
  $sectorType: String
86
91
  $buildingUsage: String
92
+ $projectScale: String
93
+ $facilityType: String
87
94
  ) {
88
95
  totalKpiYValueComprehensiveStats(
89
96
  startYearMonth: $startYearMonth
90
97
  endYearMonth: $endYearMonth
91
98
  sectorType: $sectorType
92
99
  buildingUsage: $buildingUsage
100
+ projectScale: $projectScale
101
+ facilityType: $facilityType
93
102
  ) {
94
103
  kpiName
95
104
  minVal
@@ -105,7 +114,9 @@ let KpiLeftPanel = class KpiLeftPanel extends LitElement {
105
114
  startYearMonth: this.startYearMonth,
106
115
  endYearMonth: this.endYearMonth,
107
116
  sectorType: this.sectorType || null,
108
- buildingUsage: this.buildingUsage || null
117
+ buildingUsage: this.buildingUsage || null,
118
+ projectScale: this.projectScale || null,
119
+ facilityType: this.facilityType || null
109
120
  }
110
121
  });
111
122
  this.kpiYComprehensiveStats = response.data.totalKpiYValueComprehensiveStats || [];
@@ -144,6 +155,8 @@ let KpiLeftPanel = class KpiLeftPanel extends LitElement {
144
155
  $endYearMonth: String
145
156
  $sectorType: String
146
157
  $buildingUsage: String
158
+ $projectScale: String
159
+ $facilityType: String
147
160
  ) {
148
161
  kpiZValueComprehensiveStatsByGeoGroup(
149
162
  kpiName: $kpiName
@@ -151,6 +164,8 @@ let KpiLeftPanel = class KpiLeftPanel extends LitElement {
151
164
  endYearMonth: $endYearMonth
152
165
  sectorType: $sectorType
153
166
  buildingUsage: $buildingUsage
167
+ projectScale: $projectScale
168
+ facilityType: $facilityType
154
169
  ) {
155
170
  geoGroup
156
171
  avgVal
@@ -165,7 +180,9 @@ let KpiLeftPanel = class KpiLeftPanel extends LitElement {
165
180
  startYearMonth: this.startYearMonth,
166
181
  endYearMonth: this.endYearMonth,
167
182
  sectorType: this.sectorType || null,
168
- buildingUsage: this.buildingUsage || null
183
+ buildingUsage: this.buildingUsage || null,
184
+ projectScale: this.projectScale || null,
185
+ facilityType: this.facilityType || null
169
186
  }
170
187
  });
171
188
  this.regionalKpiStats = response.data.kpiZValueComprehensiveStatsByGeoGroup || [];
@@ -202,6 +219,8 @@ let KpiLeftPanel = class KpiLeftPanel extends LitElement {
202
219
  $endYearMonth: String
203
220
  $sectorType: String
204
221
  $buildingUsage: String
222
+ $projectScale: String
223
+ $facilityType: String
205
224
  ) {
206
225
  kpiZValueMonthlyTrendByGeoGroup(
207
226
  kpiName: $kpiName
@@ -209,6 +228,8 @@ let KpiLeftPanel = class KpiLeftPanel extends LitElement {
209
228
  endYearMonth: $endYearMonth
210
229
  sectorType: $sectorType
211
230
  buildingUsage: $buildingUsage
231
+ projectScale: $projectScale
232
+ facilityType: $facilityType
212
233
  ) {
213
234
  geoGroup
214
235
  yearMonth
@@ -222,7 +243,9 @@ let KpiLeftPanel = class KpiLeftPanel extends LitElement {
222
243
  startYearMonth: this.startYearMonth,
223
244
  endYearMonth: this.endYearMonth,
224
245
  sectorType: this.sectorType || null,
225
- buildingUsage: this.buildingUsage || null
246
+ buildingUsage: this.buildingUsage || null,
247
+ projectScale: this.projectScale || null,
248
+ facilityType: this.facilityType || null
226
249
  }
227
250
  });
228
251
  this.monthlyTrendData = response.data.kpiZValueMonthlyTrendByGeoGroup || [];
@@ -559,6 +582,45 @@ let KpiLeftPanel = class KpiLeftPanel extends LitElement {
559
582
  </select>
560
583
  </div>
561
584
 
585
+ <!--
586
+ 규모·시설물유형은 줄을 따로 쓴다. 패널 폭이 400px 인데 다섯 쌍을 한 줄에 넣으면
587
+ select 하나가 70px 이 채 안 되고, "공공·교육·문화시설" 같은 항목이 잘린다.
588
+ -->
589
+ <div class="filter-row">
590
+ <span class="filter-label">규모</span>
591
+ <select
592
+ class="filter-select"
593
+ .value=${this.projectScale}
594
+ @change=${(e) => {
595
+ this.projectScale = e.target.value;
596
+ // 부모로 전파 → selectedProjectScale 동기화 → 지역 팝업도 같은 규모 필터 사용
597
+ this.dispatchEvent(new CustomEvent('scale-change', {
598
+ detail: { projectScale: this.projectScale },
599
+ bubbles: true,
600
+ composed: true
601
+ }));
602
+ }}
603
+ >
604
+ ${PROJECT_SCALE_OPTIONS.map(option => html `<option value=${option.value}>${option.label}</option>`)}
605
+ </select>
606
+ <span class="filter-label">시설물</span>
607
+ <select
608
+ class="filter-select wide"
609
+ .value=${this.facilityType}
610
+ @change=${(e) => {
611
+ this.facilityType = e.target.value;
612
+ // 부모로 전파 → selectedFacilityType 동기화 → 지역 팝업도 같은 시설물 필터 사용
613
+ this.dispatchEvent(new CustomEvent('facility-change', {
614
+ detail: { facilityType: this.facilityType },
615
+ bubbles: true,
616
+ composed: true
617
+ }));
618
+ }}
619
+ >
620
+ ${FACILITY_TYPE_OPTIONS.map(option => html `<option value=${option.value}>${option.label}</option>`)}
621
+ </select>
622
+ </div>
623
+
562
624
  <!-- 기간 필터 -->
563
625
  <div class="filter-row">
564
626
  <span class="filter-label">기간</span>
@@ -884,6 +946,10 @@ KpiLeftPanel.styles = [
884
946
  outline: none;
885
947
  border-color: #667eea;
886
948
  }
949
+ /* 시설물유형은 "공공·교육·문화시설" 처럼 항목 이름이 길어 같은 폭으로는 잘린다. */
950
+ .filter-select.wide {
951
+ flex: 1.7;
952
+ }
887
953
  `
888
954
  ];
889
955
  __decorate([
@@ -914,6 +980,14 @@ __decorate([
914
980
  property({ type: String }),
915
981
  __metadata("design:type", Object)
916
982
  ], KpiLeftPanel.prototype, "buildingUsage", void 0);
983
+ __decorate([
984
+ property({ type: String }),
985
+ __metadata("design:type", Object)
986
+ ], KpiLeftPanel.prototype, "projectScale", void 0);
987
+ __decorate([
988
+ property({ type: String }),
989
+ __metadata("design:type", Object)
990
+ ], KpiLeftPanel.prototype, "facilityType", void 0);
917
991
  __decorate([
918
992
  state(),
919
993
  __metadata("design:type", Object)