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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/_index.html +0 -5
  2. package/dist-client/components/kpi-2d-lookup-chart.d.ts +63 -0
  3. package/dist-client/components/kpi-2d-lookup-chart.js +470 -0
  4. package/dist-client/components/kpi-2d-lookup-chart.js.map +1 -0
  5. package/dist-client/google-map/common-google-map.js +10 -8
  6. package/dist-client/google-map/common-google-map.js.map +1 -1
  7. package/dist-client/pages/kpi-admin/dssp-kpi-list-page.d.ts +22 -0
  8. package/dist-client/pages/kpi-admin/dssp-kpi-list-page.js +57 -0
  9. package/dist-client/pages/kpi-admin/dssp-kpi-list-page.js.map +1 -0
  10. package/dist-client/pages/kpi-admin/kpi-grade-2d-editor.d.ts +20 -0
  11. package/dist-client/pages/kpi-admin/kpi-grade-2d-editor.js +445 -0
  12. package/dist-client/pages/kpi-admin/kpi-grade-2d-editor.js.map +1 -0
  13. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.d.ts +6 -5
  14. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js +47 -68
  15. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js.map +1 -1
  16. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.d.ts +3 -2
  17. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js +79 -122
  18. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js.map +1 -1
  19. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.d.ts +3 -2
  20. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js +71 -107
  21. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js.map +1 -1
  22. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.d.ts +4 -0
  23. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js +246 -28
  24. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js.map +1 -1
  25. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.d.ts +4 -0
  26. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js +22 -207
  27. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js.map +1 -1
  28. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.d.ts +2 -0
  29. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js +84 -25
  30. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js.map +1 -1
  31. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.d.ts +16 -0
  32. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js +261 -31
  33. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js.map +1 -1
  34. package/dist-client/pages/kpi-dashboard/kpi-dashboard.d.ts +4 -0
  35. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js +66 -4
  36. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js.map +1 -1
  37. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.d.ts +1 -2
  38. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js +1 -2
  39. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js.map +1 -1
  40. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.d.ts +1 -2
  41. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js +1 -2
  42. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js.map +1 -1
  43. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.d.ts +1 -2
  44. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js +1 -2
  45. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js.map +1 -1
  46. package/dist-client/pages/kpi-value/kpi-value-list-page.d.ts +1 -2
  47. package/dist-client/pages/kpi-value/kpi-value-list-page.js +1 -2
  48. package/dist-client/pages/kpi-value/kpi-value-list-page.js.map +1 -1
  49. package/dist-client/pages/sv-project-detail.d.ts +1 -0
  50. package/dist-client/pages/sv-project-detail.js +26 -13
  51. package/dist-client/pages/sv-project-detail.js.map +1 -1
  52. package/dist-client/pages/sv-project-list.js +6 -6
  53. package/dist-client/pages/sv-project-list.js.map +1 -1
  54. package/dist-client/route.d.ts +1 -1
  55. package/dist-client/route.js +4 -0
  56. package/dist-client/route.js.map +1 -1
  57. package/dist-client/tsconfig.tsbuildinfo +1 -1
  58. package/dist-client/viewparts/menu-tools.d.ts +1 -2
  59. package/dist-client/viewparts/menu-tools.js +1 -2
  60. package/dist-client/viewparts/menu-tools.js.map +1 -1
  61. package/dist-server/scripts/calculate-kpi-scores.js +65 -3
  62. package/dist-server/scripts/calculate-kpi-scores.js.map +1 -1
  63. package/dist-server/scripts/load-grade-data-migration.d.ts +4 -0
  64. package/dist-server/scripts/load-grade-data-migration.js +95 -10
  65. package/dist-server/scripts/load-grade-data-migration.js.map +1 -1
  66. package/dist-server/scripts/propagate-parent-kpi-values.js +58 -4
  67. package/dist-server/scripts/propagate-parent-kpi-values.js.map +1 -1
  68. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.d.ts +6 -0
  69. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js +57 -7
  70. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js.map +1 -1
  71. package/dist-server/service/kpi-stat/kpi-stat-query.d.ts +8 -5
  72. package/dist-server/service/kpi-stat/kpi-stat-query.js +228 -11
  73. package/dist-server/service/kpi-stat/kpi-stat-query.js.map +1 -1
  74. package/dist-server/service/kpi-stat/kpi-stat-types.d.ts +13 -0
  75. package/dist-server/service/kpi-stat/kpi-stat-types.js +51 -1
  76. package/dist-server/service/kpi-stat/kpi-stat-types.js.map +1 -1
  77. package/dist-server/tsconfig.tsbuildinfo +1 -1
  78. package/package.json +54 -54
  79. package/schema.graphql +95 -58
  80. package/things-factory.config.js +3 -1
  81. package/views/auth-page.html +0 -1
  82. package/views/public/home.html +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kpi-grade-2d-editor.js","sourceRoot":"","sources":["../../../client/pages/kpi-admin/kpi-grade-2d-editor.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/button/filled-button.js'\nimport '@material/web/icon/icon.js'\nimport '@operato/data-grist/ox-grist.js'\n\nimport gql from 'graphql-tag'\nimport * as d3 from 'd3'\nimport { LitElement, css, html } from 'lit'\nimport { customElement, property, state, query } from 'lit/decorators.js'\nimport { client } from '@operato/graphql'\nimport { notify } from '@operato/layout'\nimport { CommonHeaderStyles } from '@operato/styles'\nimport { DataGrist, FetchOption } from '@operato/data-grist'\n\ninterface Grade2DRow {\n progressRate: number\n boundary5to4: number\n boundary4to3: number\n boundary3to2: number\n boundary2to1: number\n}\n\n// 점수 구간 정의 (높은 점수 = 좋음 = 낮은 공기편차)\nconst SCORE_ZONES = [\n { score: 5, label: '5점', color: '#1b5e20', fillColor: 'rgba(76,175,80,0.15)' },\n { score: 4, label: '4점', color: '#33691e', fillColor: 'rgba(139,195,74,0.15)' },\n { score: 3, label: '3점', color: '#e65100', fillColor: 'rgba(255,193,7,0.15)' },\n { score: 2, label: '2점', color: '#bf360c', fillColor: 'rgba(255,152,0,0.15)' },\n { score: 1, label: '1점', color: '#b71c1c', fillColor: 'rgba(244,67,54,0.15)' }\n]\n\nconst BOUNDARIES = [\n { key: 'boundary5to4' as const, label: '5점 기준선', subLabel: '이하 → 5점', color: '#4caf50' },\n { key: 'boundary4to3' as const, label: '4점 기준선', subLabel: '이하 → 4점', color: '#8bc34a' },\n { key: 'boundary3to2' as const, label: '3점 기준선', subLabel: '이하 → 3점', color: '#ff9800' },\n { key: 'boundary2to1' as const, label: '2점 기준선', subLabel: '이하 → 2점', color: '#f44336' }\n]\n\n@customElement('kpi-grade-2d-editor')\nexport class KpiGrade2dEditor extends LitElement {\n static styles = [\n CommonHeaderStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n height: 100%;\n background-color: var(--md-sys-color-surface);\n }\n\n .info-bar {\n display: flex;\n align-items: center;\n gap: 24px;\n padding: 8px 16px;\n background: var(--md-sys-color-surface-variant, #f5f5f5);\n border-bottom: 1px solid var(--md-sys-color-outline-variant, #ddd);\n font-size: 12px;\n color: var(--md-sys-color-on-surface-variant, #555);\n flex-shrink: 0;\n }\n\n .info-bar .rule {\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .info-bar .arrow {\n color: #888;\n }\n\n .score-badge {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 20px;\n border-radius: 50%;\n font-size: 11px;\n font-weight: bold;\n color: #fff;\n }\n\n .chart-wrap {\n padding: 8px 16px 0;\n flex-shrink: 0;\n position: relative;\n }\n\n .chart-wrap svg {\n width: 100%;\n display: block;\n }\n\n .legend {\n display: flex;\n gap: 20px;\n padding: 4px 16px 8px;\n font-size: 12px;\n flex-shrink: 0;\n flex-wrap: wrap;\n }\n\n .legend-item {\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .legend-line {\n width: 20px;\n height: 3px;\n border-radius: 2px;\n }\n\n .legend-sub {\n color: #888;\n font-size: 11px;\n }\n\n ox-grist {\n flex: 1;\n overflow: hidden;\n }\n\n .footer {\n display: flex;\n justify-content: flex-end;\n gap: 8px;\n padding: 8px 16px;\n border-top: 1px solid var(--md-sys-color-outline-variant, #ccc);\n flex-shrink: 0;\n }\n `\n ]\n\n @property({ type: Object }) kpi: any\n @state() private rows: Grade2DRow[] = []\n @state() private gristConfig: any = null\n @query('ox-grist') private grist!: DataGrist\n @query('svg') private svgEl!: SVGElement\n\n firstUpdated() {\n const grades = this.kpi?.grades\n this.rows = grades?.rows ? grades.rows.map((r: any) => ({ ...r })) : []\n\n this.gristConfig = {\n list: { fields: ['progressRate', 'boundary5to4', 'boundary4to3', 'boundary3to2', 'boundary2to1'] },\n columns: [\n { type: 'gutter', gutterName: 'sequence', fixed: true },\n {\n type: 'number',\n name: 'progressRate',\n header: '공정률 (%)',\n record: { editable: false },\n sortable: false,\n width: 90\n },\n {\n type: 'number',\n name: 'boundary5to4',\n header: '5점 기준선',\n record: { editable: true, align: 'right' },\n width: 110\n },\n {\n type: 'number',\n name: 'boundary4to3',\n header: '4점 기준선',\n record: { editable: true, align: 'right' },\n width: 110\n },\n {\n type: 'number',\n name: 'boundary3to2',\n header: '3점 기준선',\n record: { editable: true, align: 'right' },\n width: 110\n },\n {\n type: 'number',\n name: 'boundary2to1',\n header: '2점 기준선',\n record: { editable: true, align: 'right' },\n width: 110\n }\n ],\n rows: { selectable: false, appendable: false },\n pagination: { infinite: true }\n }\n\n requestAnimationFrame(() => this._drawChart(this.rows))\n }\n\n render() {\n return html`\n <div class=\"info-bar\">\n <span><strong>X축</strong>: 공정률 (%)</span>\n <span><strong>Y축</strong>: 공기편차 (%)</span>\n <span class=\"arrow\">|</span>\n <span class=\"rule\">\n 공기편차\n <span class=\"score-badge\" style=\"background:#4caf50\">5</span>\n 기준선 미만 → 5점,\n <span class=\"score-badge\" style=\"background:#8bc34a\">4</span>\n 기준선 미만 → 4점,\n <span class=\"score-badge\" style=\"background:#ff9800\">3</span>\n 기준선 미만 → 3점,\n <span class=\"score-badge\" style=\"background:#f44336\">2</span>\n 기준선 미만 → 2점, 초과 → 1점\n </span>\n </div>\n\n <div class=\"chart-wrap\">\n <svg height=\"220\"></svg>\n </div>\n\n <div class=\"legend\">\n ${BOUNDARIES.map(\n b => html`\n <div class=\"legend-item\">\n <div class=\"legend-line\" style=\"background:${b.color}\"></div>\n <span>${b.label}</span>\n <span class=\"legend-sub\">(${b.subLabel})</span>\n </div>\n `\n )}\n </div>\n\n <ox-grist\n .mode=${'GRID'}\n .config=${this.gristConfig}\n .fetchHandler=${this._fetchHandler.bind(this)}\n @change=${this._onGristChange}\n ></ox-grist>\n\n <div class=\"footer\">\n <md-filled-button @click=${this._save}>\n <md-icon slot=\"icon\">save</md-icon>\n 저장\n </md-filled-button>\n </div>\n `\n }\n\n private async _fetchHandler({ page, limit }: FetchOption) {\n return { total: this.rows.length, records: this.rows }\n }\n\n private _onGristChange() {\n this._drawChart(this._getCurrentRows())\n }\n\n private _getCurrentRows(): Grade2DRow[] {\n const records = this.grist?.dirtyData?.records\n if (!records || records.length === 0) return this.rows\n\n return records.map((r: any) => ({\n progressRate: r.progressRate,\n boundary5to4: Number(r.boundary5to4),\n boundary4to3: Number(r.boundary4to3),\n boundary3to2: Number(r.boundary3to2),\n boundary2to1: Number(r.boundary2to1)\n }))\n }\n\n private _drawChart(rows: Grade2DRow[]) {\n if (!this.svgEl || !rows.length) return\n\n const W = this.svgEl.clientWidth || 560\n const H = 220\n const m = { top: 16, right: 32, bottom: 36, left: 56 }\n const w = W - m.left - m.right\n const h = H - m.top - m.bottom\n\n const svg = d3.select(this.svgEl).attr('height', H)\n svg.selectAll('*').remove()\n\n const g = svg.append('g').attr('transform', `translate(${m.left},${m.top})`)\n\n const x = d3.scaleLinear().domain([0, 99]).range([0, w])\n\n const allVals = rows.flatMap(r => [r.boundary5to4, r.boundary4to3, r.boundary3to2, r.boundary2to1])\n const [yMin, yMax] = d3.extent(allVals) as [number, number]\n const pad = (yMax - yMin) * 0.15 || 5\n const y = d3.scaleLinear().domain([yMin - pad, yMax + pad]).range([h, 0])\n\n // 점수 구간 채우기 (5개 존)\n const zoneDefs: Array<{ y0: (r: Grade2DRow) => number; y1: (r: Grade2DRow) => number; fill: string; label: string }> = [\n { y0: _r => h, y1: r => y(r.boundary5to4), fill: 'rgba(76,175,80,0.12)', label: '5점' },\n { y0: r => y(r.boundary5to4), y1: r => y(r.boundary4to3), fill: 'rgba(139,195,74,0.12)', label: '4점' },\n { y0: r => y(r.boundary4to3), y1: r => y(r.boundary3to2), fill: 'rgba(255,193,7,0.12)', label: '3점' },\n { y0: r => y(r.boundary3to2), y1: r => y(r.boundary2to1), fill: 'rgba(255,152,0,0.12)', label: '2점' },\n { y0: r => y(r.boundary2to1), y1: _r => 0, fill: 'rgba(244,67,54,0.12)', label: '1점' }\n ]\n\n for (const zone of zoneDefs) {\n const area = d3.area<Grade2DRow>()\n .x(r => x(r.progressRate))\n .y0(zone.y0)\n .y1(zone.y1)\n .curve(d3.curveMonotoneX)\n\n g.append('path')\n .datum(rows)\n .attr('fill', zone.fill)\n .attr('d', area)\n }\n\n // 중간 공정률 지점에 점수 레이블\n const midRow = rows[Math.floor(rows.length / 2)]\n if (midRow) {\n const scoreColors = ['#2e7d32', '#558b2f', '#f57f17', '#e65100', '#c62828']\n const scorePositions = [\n (h + y(midRow.boundary5to4)) / 2,\n (y(midRow.boundary5to4) + y(midRow.boundary4to3)) / 2,\n (y(midRow.boundary4to3) + y(midRow.boundary3to2)) / 2,\n (y(midRow.boundary3to2) + y(midRow.boundary2to1)) / 2,\n y(midRow.boundary2to1) / 2\n ]\n const scoreLabels = ['5점', '4점', '3점', '2점', '1점']\n\n scorePositions.forEach((yPos, i) => {\n if (yPos > 4 && yPos < h - 4) {\n g.append('text')\n .attr('x', w - 4)\n .attr('y', yPos)\n .attr('text-anchor', 'end')\n .attr('dominant-baseline', 'middle')\n .attr('font-size', '10px')\n .attr('font-weight', 'bold')\n .attr('fill', scoreColors[i])\n .attr('opacity', 0.8)\n .text(scoreLabels[i])\n }\n })\n }\n\n // 격자선\n g.append('g')\n .attr('stroke', '#e0e0e0')\n .attr('stroke-dasharray', '2,3')\n .selectAll('line')\n .data(y.ticks(5))\n .join('line')\n .attr('x1', 0).attr('x2', w)\n .attr('y1', d => y(d)).attr('y2', d => y(d))\n\n // y=0 기준선 (공기편차 0%)\n const yZero = y(0)\n if (yZero >= 0 && yZero <= h) {\n g.append('line')\n .attr('x1', 0).attr('x2', w)\n .attr('y1', yZero).attr('y2', yZero)\n .attr('stroke', '#90a4ae')\n .attr('stroke-width', 1)\n .attr('stroke-dasharray', '4,2')\n g.append('text')\n .attr('x', -4)\n .attr('y', yZero)\n .attr('text-anchor', 'end')\n .attr('dominant-baseline', 'middle')\n .attr('font-size', '9px')\n .attr('fill', '#90a4ae')\n .text('0%')\n }\n\n // X축\n g.append('g')\n .attr('transform', `translate(0,${h})`)\n .call(d3.axisBottom(x).ticks(10).tickFormat(d => `${d}%`))\n .selectAll('text')\n .style('font-size', '10px')\n\n // X축 레이블\n g.append('text')\n .attr('x', w / 2)\n .attr('y', h + 30)\n .attr('text-anchor', 'middle')\n .attr('font-size', '11px')\n .attr('fill', '#666')\n .text('공정률 (%)')\n\n // Y축\n g.append('g')\n .call(d3.axisLeft(y).ticks(5))\n .selectAll('text')\n .style('font-size', '10px')\n\n // Y축 레이블\n g.append('text')\n .attr('transform', 'rotate(-90)')\n .attr('x', -h / 2)\n .attr('y', -44)\n .attr('text-anchor', 'middle')\n .attr('font-size', '11px')\n .attr('fill', '#666')\n .text('공기편차 (%)')\n\n // 4개 경계선\n for (const { key, color } of BOUNDARIES) {\n const line = d3\n .line<Grade2DRow>()\n .x(r => x(r.progressRate))\n .y(r => y(r[key]))\n .curve(d3.curveMonotoneX)\n\n g.append('path')\n .datum(rows)\n .attr('fill', 'none')\n .attr('stroke', color)\n .attr('stroke-width', 2)\n .attr('d', line)\n }\n }\n\n private _validate(rows: Grade2DRow[]): boolean {\n for (const row of rows) {\n const { progressRate: p, boundary5to4: b54, boundary4to3: b43, boundary3to2: b32, boundary2to1: b21 } = row\n if (!(b54 < b43 && b43 < b32 && b32 < b21)) {\n notify({\n message: `공정률 ${p}%행: 기준선 값이 잘못됐습니다. 5점 < 4점 < 3점 < 2점 기준선 순서여야 합니다.`,\n level: 'error'\n })\n return false\n }\n }\n return true\n }\n\n private async _save() {\n const rows = this._getCurrentRows()\n if (!this._validate(rows)) return\n\n const grades = { type: 'PROGRESS_DEVIATION_LOOKUP', rows }\n\n try {\n await client.mutate({\n mutation: gql`\n mutation ($id: String!, $patch: KpiPatch!) {\n updateKpi(id: $id, patch: $patch) {\n id\n grades\n scoreType\n }\n }\n `,\n variables: {\n id: this.kpi.id,\n patch: { grades, scoreType: 'CUSTOM' }\n }\n })\n\n this.rows = rows\n this._drawChart(rows)\n notify({ message: '저장되었습니다.' })\n } catch (e) {\n notify({ message: '저장 중 오류가 발생했습니다.', level: 'error' })\n }\n }\n}\n"]}
@@ -1,16 +1,17 @@
1
1
  import { LitElement } from 'lit';
2
2
  export declare class KpiLevel1Card extends LitElement {
3
3
  static styles: import("lit").CSSResult;
4
+ sectorType: string;
5
+ buildingUsage: string;
4
6
  loading: boolean;
5
7
  error: string;
6
8
  totalScore: number;
7
9
  grade: string;
8
- totalKpis: number;
9
- totalCategories: number;
10
- averageScore: number;
11
- currentMonth: string;
10
+ projectCount: number;
11
+ minScore: number;
12
+ maxScore: number;
12
13
  connectedCallback(): void;
13
- private getCurrentMonth;
14
+ updated(changedProperties: Map<string, unknown>): void;
14
15
  private calculateGrade;
15
16
  fetchTotalScore(): Promise<void>;
16
17
  render(): import("lit-html").TemplateResult<1>;
@@ -1,31 +1,30 @@
1
1
  import { __decorate, __metadata } from "tslib";
2
2
  import { html, css } from 'lit';
3
- import { customElement, state } from 'lit/decorators.js';
3
+ import { customElement, state, property } from 'lit/decorators.js';
4
4
  import { LitElement } from 'lit';
5
5
  import { client } from '@operato/graphql';
6
6
  import gql from 'graphql-tag';
7
7
  let KpiLevel1Card = class KpiLevel1Card extends LitElement {
8
8
  constructor() {
9
9
  super(...arguments);
10
+ this.sectorType = '';
11
+ this.buildingUsage = '';
10
12
  this.loading = true;
11
13
  this.error = '';
12
14
  this.totalScore = 0;
13
15
  this.grade = '';
14
- this.totalKpis = 0;
15
- this.totalCategories = 0;
16
- this.averageScore = 0;
17
- this.currentMonth = '';
16
+ this.projectCount = 0;
17
+ this.minScore = 0;
18
+ this.maxScore = 0;
18
19
  }
19
20
  connectedCallback() {
20
21
  super.connectedCallback();
21
- this.currentMonth = this.getCurrentMonth();
22
22
  this.fetchTotalScore();
23
23
  }
24
- getCurrentMonth() {
25
- const now = new Date();
26
- const year = now.getFullYear();
27
- const month = String(now.getMonth() + 1).padStart(2, '0');
28
- return `${year}-${month}`;
24
+ updated(changedProperties) {
25
+ if (changedProperties.has('sectorType') || changedProperties.has('buildingUsage')) {
26
+ this.fetchTotalScore();
27
+ }
29
28
  }
30
29
  calculateGrade(score) {
31
30
  if (score >= 90)
@@ -54,55 +53,32 @@ let KpiLevel1Card = class KpiLevel1Card extends LitElement {
54
53
  try {
55
54
  const response = await client.query({
56
55
  query: gql `
57
- query {
58
- kpiStatistics {
59
- items {
60
- id
61
- valueDate
62
- periodType
63
- mean
64
- median
65
- kpi {
66
- id
67
- name
68
- targetValue
69
- unit
70
- category: parent {
71
- id
72
- name
73
- }
74
- }
75
- }
56
+ query KpiLevel1Stats($sectorType: String, $buildingUsage: String) {
57
+ kpiProjectStats(kpiNamePattern: "Z", sectorType: $sectorType, buildingUsage: $buildingUsage) {
58
+ kpiName
59
+ avgVal
60
+ minVal
61
+ maxVal
62
+ projectCount
76
63
  }
77
64
  }
78
- `
65
+ `,
66
+ variables: {
67
+ sectorType: this.sectorType || null,
68
+ buildingUsage: this.buildingUsage || null
69
+ }
79
70
  });
80
- const statistics = response.data.kpiStatistics.items || [];
81
- // MONTH 타입의 현재 월 데이터만 필터링
82
- const currentMonthStats = statistics.filter(stat => stat.periodType === 'MONTH' && stat.valueDate === this.currentMonth);
83
- if (currentMonthStats.length === 0) {
84
- this.error = '현재 월의 데이터가 없습니다.';
71
+ const stats = response.data.kpiProjectStats || [];
72
+ if (stats.length === 0) {
73
+ this.error = '데이터가 없습니다.';
85
74
  return;
86
75
  }
87
- // 총점 계산 (평균값 기준)
88
- const scores = currentMonthStats.map(stat => {
89
- var _a;
90
- const mean = stat.mean || 0;
91
- const targetValue = ((_a = stat.kpi) === null || _a === void 0 ? void 0 : _a.targetValue) || 100;
92
- if (targetValue === 0)
93
- return 0;
94
- // 목표 대비 달성률 계산 (최대 100점)
95
- const achievement = Math.min((mean / targetValue) * 100, 100);
96
- return Math.max(achievement, 0); // 음수 방지
97
- });
98
- this.totalScore = Math.round(scores.reduce((sum, score) => sum + score, 0) / scores.length);
76
+ const zStat = stats[0];
77
+ this.totalScore = Math.round(zStat.avgVal * 100);
99
78
  this.grade = this.calculateGrade(this.totalScore);
100
- this.totalKpis = currentMonthStats.length;
101
- // 카테고리 계산
102
- const categories = new Set(currentMonthStats.map(stat => { var _a, _b; return (_b = (_a = stat.kpi) === null || _a === void 0 ? void 0 : _a.category) === null || _b === void 0 ? void 0 : _b.name; }).filter(Boolean));
103
- this.totalCategories = categories.size;
104
- // 평균 점수 계산
105
- this.averageScore = Math.round(scores.reduce((sum, score) => sum + score, 0) / scores.length);
79
+ this.projectCount = zStat.projectCount;
80
+ this.minScore = Math.round(zStat.minVal * 100);
81
+ this.maxScore = Math.round(zStat.maxVal * 100);
106
82
  }
107
83
  catch (e) {
108
84
  console.error('총점 데이터를 불러오지 못했습니다:', e);
@@ -133,20 +109,19 @@ let KpiLevel1Card = class KpiLevel1Card extends LitElement {
133
109
  <div class="score-title">그룹 총점</div>
134
110
  <div class="score-value">${this.totalScore}</div>
135
111
  <div class="score-grade">${this.grade}</div>
136
- <div class="score-period">${this.currentMonth} 기준</div>
137
112
 
138
113
  <div class="score-details">
139
114
  <div class="detail-item">
140
- <div class="detail-value">${this.totalKpis}</div>
141
- <div class="detail-label">KPI</div>
115
+ <div class="detail-value">${this.projectCount}</div>
116
+ <div class="detail-label">분석 프로젝트</div>
142
117
  </div>
143
118
  <div class="detail-item">
144
- <div class="detail-value">${this.totalCategories}</div>
145
- <div class="detail-label">카테고리</div>
119
+ <div class="detail-value">${this.minScore}</div>
120
+ <div class="detail-label">최저</div>
146
121
  </div>
147
122
  <div class="detail-item">
148
- <div class="detail-value">${this.averageScore}</div>
149
- <div class="detail-label">평균</div>
123
+ <div class="detail-value">${this.maxScore}</div>
124
+ <div class="detail-label">최고</div>
150
125
  </div>
151
126
  </div>
152
127
  </div>
@@ -241,6 +216,14 @@ KpiLevel1Card.styles = css `
241
216
  font-size: 1.1rem;
242
217
  }
243
218
  `;
219
+ __decorate([
220
+ property({ type: String }),
221
+ __metadata("design:type", String)
222
+ ], KpiLevel1Card.prototype, "sectorType", void 0);
223
+ __decorate([
224
+ property({ type: String }),
225
+ __metadata("design:type", String)
226
+ ], KpiLevel1Card.prototype, "buildingUsage", void 0);
244
227
  __decorate([
245
228
  state(),
246
229
  __metadata("design:type", Object)
@@ -260,19 +243,15 @@ __decorate([
260
243
  __decorate([
261
244
  state(),
262
245
  __metadata("design:type", Object)
263
- ], KpiLevel1Card.prototype, "totalKpis", void 0);
264
- __decorate([
265
- state(),
266
- __metadata("design:type", Object)
267
- ], KpiLevel1Card.prototype, "totalCategories", void 0);
246
+ ], KpiLevel1Card.prototype, "projectCount", void 0);
268
247
  __decorate([
269
248
  state(),
270
249
  __metadata("design:type", Object)
271
- ], KpiLevel1Card.prototype, "averageScore", void 0);
250
+ ], KpiLevel1Card.prototype, "minScore", void 0);
272
251
  __decorate([
273
252
  state(),
274
253
  __metadata("design:type", Object)
275
- ], KpiLevel1Card.prototype, "currentMonth", void 0);
254
+ ], KpiLevel1Card.prototype, "maxScore", void 0);
276
255
  KpiLevel1Card = __decorate([
277
256
  customElement('kpi-level1-card')
278
257
  ], KpiLevel1Card);
@@ -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 } 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 @state() loading = true\n @state() error = ''\n @state() totalScore = 0\n @state() grade = ''\n @state() totalKpis = 0\n @state() totalCategories = 0\n @state() averageScore = 0\n @state() currentMonth = ''\n\n connectedCallback() {\n super.connectedCallback()\n this.currentMonth = this.getCurrentMonth()\n this.fetchTotalScore()\n }\n\n private getCurrentMonth(): string {\n const now = new Date()\n const year = now.getFullYear()\n const month = String(now.getMonth() + 1).padStart(2, '0')\n return `${year}-${month}`\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 {\n kpiStatistics {\n items {\n id\n valueDate\n periodType\n mean\n median\n kpi {\n id\n name\n targetValue\n unit\n category: parent {\n id\n name\n }\n }\n }\n }\n }\n `\n })\n\n const statistics = response.data.kpiStatistics.items || []\n\n // MONTH 타입의 현재 월 데이터만 필터링\n const currentMonthStats = statistics.filter(\n stat => stat.periodType === 'MONTH' && stat.valueDate === this.currentMonth\n )\n\n if (currentMonthStats.length === 0) {\n this.error = '현재 월의 데이터가 없습니다.'\n return\n }\n\n // 총점 계산 (평균값 기준)\n const scores = currentMonthStats.map(stat => {\n const mean = stat.mean || 0\n const targetValue = stat.kpi?.targetValue || 100\n\n if (targetValue === 0) return 0\n\n // 목표 대비 달성률 계산 (최대 100점)\n const achievement = Math.min((mean / targetValue) * 100, 100)\n return Math.max(achievement, 0) // 음수 방지\n })\n\n this.totalScore = Math.round(scores.reduce((sum, score) => sum + score, 0) / scores.length)\n this.grade = this.calculateGrade(this.totalScore)\n this.totalKpis = currentMonthStats.length\n\n // 카테고리 수 계산\n const categories = new Set(currentMonthStats.map(stat => stat.kpi?.category?.name).filter(Boolean))\n this.totalCategories = categories.size\n\n // 평균 점수 계산\n this.averageScore = Math.round(scores.reduce((sum, score) => sum + score, 0) / scores.length)\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 <div class=\"score-period\">${this.currentMonth} 기준</div>\n\n <div class=\"score-details\">\n <div class=\"detail-item\">\n <div class=\"detail-value\">${this.totalKpis}</div>\n <div class=\"detail-label\">KPI</div>\n </div>\n <div class=\"detail-item\">\n <div class=\"detail-value\">${this.totalCategories}</div>\n <div class=\"detail-label\">카테고리</div>\n </div>\n <div class=\"detail-item\">\n <div class=\"detail-value\">${this.averageScore}</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\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"]}
@@ -3,17 +3,18 @@ import '../../components/kpi-radar-chart';
3
3
  import '../../components/kpi-boxplot-chart';
4
4
  export declare class KpiLevel2Comparison extends LitElement {
5
5
  static styles: import("lit").CSSResult;
6
+ sectorType: string;
7
+ buildingUsage: string;
6
8
  loading: boolean;
7
9
  error: string;
8
10
  radarData: any[];
9
11
  boxplotData: any[];
10
12
  categories: string[];
11
- currentMonth: string;
12
13
  totalCategories: number;
13
14
  totalKpis: number;
14
15
  averageScore: number;
15
16
  connectedCallback(): void;
16
- private getCurrentMonth;
17
+ updated(changedProperties: Map<string, unknown>): void;
17
18
  fetchCategoryComparison(): Promise<void>;
18
19
  private generateRadarData;
19
20
  private generateBoxplotData;
@@ -1,6 +1,6 @@
1
1
  import { __decorate, __metadata } from "tslib";
2
2
  import { html, css } from 'lit';
3
- import { customElement, state } from 'lit/decorators.js';
3
+ import { customElement, state, property } from 'lit/decorators.js';
4
4
  import { LitElement } from 'lit';
5
5
  import { client } from '@operato/graphql';
6
6
  import gql from 'graphql-tag';
@@ -9,26 +9,25 @@ import '../../components/kpi-boxplot-chart';
9
9
  let KpiLevel2Comparison = class KpiLevel2Comparison extends LitElement {
10
10
  constructor() {
11
11
  super(...arguments);
12
+ this.sectorType = '';
13
+ this.buildingUsage = '';
12
14
  this.loading = true;
13
15
  this.error = '';
14
16
  this.radarData = [];
15
17
  this.boxplotData = [];
16
18
  this.categories = [];
17
- this.currentMonth = '';
18
19
  this.totalCategories = 0;
19
20
  this.totalKpis = 0;
20
21
  this.averageScore = 0;
21
22
  }
22
23
  connectedCallback() {
23
24
  super.connectedCallback();
24
- this.currentMonth = this.getCurrentMonth();
25
25
  this.fetchCategoryComparison();
26
26
  }
27
- getCurrentMonth() {
28
- const now = new Date();
29
- const year = now.getFullYear();
30
- const month = String(now.getMonth() + 1).padStart(2, '0');
31
- return `${year}-${month}`;
27
+ updated(changedProperties) {
28
+ if (changedProperties.has('sectorType') || changedProperties.has('buildingUsage')) {
29
+ this.fetchCategoryComparison();
30
+ }
32
31
  }
33
32
  async fetchCategoryComparison() {
34
33
  this.loading = true;
@@ -36,71 +35,46 @@ let KpiLevel2Comparison = class KpiLevel2Comparison extends LitElement {
36
35
  try {
37
36
  const response = await client.query({
38
37
  query: gql `
39
- query {
40
- kpiStatistics {
41
- items {
42
- id
43
- valueDate
44
- periodType
45
- mean
46
- median
47
- standardDeviation
48
- minimum
49
- maximum
50
- percentile25
51
- percentile75
52
- kpi {
53
- id
54
- name
55
- targetValue
56
- unit
57
- category: parent {
58
- id
59
- name
60
- }
61
- }
62
- }
38
+ query KpiLevel2Stats($sectorType: String, $buildingUsage: String) {
39
+ kpiProjectStats(kpiNamePattern: "X", sectorType: $sectorType, buildingUsage: $buildingUsage) {
40
+ kpiName
41
+ categoryName
42
+ minVal
43
+ q1Val
44
+ medVal
45
+ q3Val
46
+ maxVal
47
+ avgVal
48
+ stddevVal
49
+ projectCount
63
50
  }
64
51
  }
65
- `
52
+ `,
53
+ variables: {
54
+ sectorType: this.sectorType || null,
55
+ buildingUsage: this.buildingUsage || null
56
+ }
66
57
  });
67
- const statistics = response.data.kpiStatistics.items || [];
68
- // MONTH 타입의 현재 월 데이터만 필터링
69
- const currentMonthStats = statistics.filter(stat => stat.periodType === 'MONTH' && stat.valueDate === this.currentMonth);
70
- if (currentMonthStats.length === 0) {
71
- this.error = '현재 월의 데이터가 없습니다.';
58
+ const stats = response.data.kpiProjectStats || [];
59
+ if (stats.length === 0) {
60
+ this.error = '데이터가 없습니다.';
72
61
  return;
73
62
  }
74
- // 카테고리별로 데이터 그룹화
75
- const categoryStats = new Map();
76
- currentMonthStats.forEach(stat => {
77
- var _a, _b;
78
- if ((_b = (_a = stat.kpi) === null || _a === void 0 ? void 0 : _a.category) === null || _b === void 0 ? void 0 : _b.name) {
79
- const categoryName = stat.kpi.category.name;
80
- if (!categoryStats.has(categoryName)) {
81
- categoryStats.set(categoryName, []);
82
- }
83
- categoryStats.get(categoryName).push(stat);
84
- }
63
+ // X-level KPIs를 categoryName(Y-level)으로 그룹화
64
+ const categoryMap = new Map();
65
+ stats.forEach(stat => {
66
+ const catName = stat.categoryName || '기타';
67
+ if (!categoryMap.has(catName))
68
+ categoryMap.set(catName, []);
69
+ categoryMap.get(catName).push(stat);
85
70
  });
86
- this.categories = Array.from(categoryStats.keys());
71
+ this.categories = Array.from(categoryMap.keys());
87
72
  this.totalCategories = this.categories.length;
88
- this.totalKpis = currentMonthStats.length;
89
- // 레이더 차트 데이터 생성
90
- this.generateRadarData(categoryStats);
91
- // 박스플롯 데이터 생성
92
- this.generateBoxplotData(categoryStats);
93
- // 평균 점수 계산
94
- const scores = currentMonthStats.map(stat => {
95
- var _a;
96
- const mean = stat.mean || 0;
97
- const targetValue = ((_a = stat.kpi) === null || _a === void 0 ? void 0 : _a.targetValue) || 100;
98
- if (targetValue === 0)
99
- return 0;
100
- const achievement = Math.min((mean / targetValue) * 100, 100);
101
- return Math.max(achievement, 0);
102
- });
103
- this.averageScore = Math.round(scores.reduce((sum, score) => sum + score, 0) / scores.length);
73
+ this.totalKpis = stats.length;
74
+ this.generateRadarData(categoryMap);
75
+ this.generateBoxplotData(categoryMap);
76
+ const allAvg = stats.map(s => s.avgVal * 100);
77
+ this.averageScore = Math.round(allAvg.reduce((a, b) => a + b, 0) / allAvg.length);
104
78
  }
105
79
  catch (e) {
106
80
  console.error('카테고리 비교 데이터를 불러오지 못했습니다:', e);
@@ -110,67 +84,46 @@ let KpiLevel2Comparison = class KpiLevel2Comparison extends LitElement {
110
84
  this.loading = false;
111
85
  }
112
86
  }
113
- generateRadarData(categoryStats) {
87
+ generateRadarData(categoryMap) {
114
88
  const result = [];
115
89
  this.categories.forEach(category => {
116
- const stats = categoryStats.get(category);
117
- const means = stats.map(s => s.mean || 0).filter(v => v > 0);
118
- const medians = stats.map(s => s.median || 0).filter(v => v > 0);
119
- const stdDevs = stats.map(s => s.standardDeviation || 0).filter(v => v > 0);
120
- if (means.length > 0) {
121
- result.push({
122
- group: '평균',
123
- category,
124
- value: means.reduce((a, b) => a + b, 0) / means.length
125
- });
90
+ const stats = categoryMap.get(category);
91
+ const avgVals = stats.map(s => s.avgVal).filter(v => v > 0);
92
+ const medVals = stats.map(s => s.medVal).filter(v => v > 0);
93
+ const stddevVals = stats.map(s => s.stddevVal).filter(v => v > 0);
94
+ if (avgVals.length > 0) {
95
+ result.push({ group: '평균', category, value: avgVals.reduce((a, b) => a + b, 0) / avgVals.length });
126
96
  }
127
- if (medians.length > 0) {
128
- result.push({
129
- group: '중앙값',
130
- category,
131
- value: medians.reduce((a, b) => a + b, 0) / medians.length
132
- });
97
+ if (medVals.length > 0) {
98
+ result.push({ group: '중앙값', category, value: medVals.reduce((a, b) => a + b, 0) / medVals.length });
133
99
  }
134
- if (stdDevs.length > 0) {
135
- result.push({
136
- group: '표준편차',
137
- category,
138
- value: stdDevs.reduce((a, b) => a + b, 0) / stdDevs.length
139
- });
100
+ if (stddevVals.length > 0) {
101
+ result.push({ group: '표준편차', category, value: stddevVals.reduce((a, b) => a + b, 0) / stddevVals.length });
140
102
  }
141
103
  });
142
104
  this.radarData = result;
143
105
  }
144
- generateBoxplotData(categoryStats) {
106
+ generateBoxplotData(categoryMap) {
145
107
  const result = [];
146
108
  this.categories.forEach(category => {
147
- const stats = categoryStats.get(category);
148
- // KPI의 평균값들을 수집
149
- const allMeans = stats.map(s => s.mean || 0).filter(v => v > 0);
150
- const allMedians = stats.map(s => s.median || 0).filter(v => v > 0);
151
- const allMins = stats.map(s => s.minimum || 0).filter(v => v > 0);
152
- const allMaxs = stats.map(s => s.maximum || 0).filter(v => v > 0);
153
- const allQ1s = stats.map(s => s.percentile25 || 0).filter(v => v > 0);
154
- const allQ3s = stats.map(s => s.percentile75 || 0).filter(v => v > 0);
155
- if (allMeans.length > 0) {
156
- const sortedMeans = [...allMeans].sort((a, b) => a - b);
157
- const min = sortedMeans[0];
158
- const max = sortedMeans[sortedMeans.length - 1];
159
- const mean = allMeans.reduce((a, b) => a + b, 0) / allMeans.length;
160
- const median = allMedians.length > 0 ? allMedians.reduce((a, b) => a + b, 0) / allMedians.length : mean;
161
- const q1 = sortedMeans[Math.floor(sortedMeans.length / 4)];
162
- const q3 = sortedMeans[Math.floor((sortedMeans.length * 3) / 4)];
163
- result.push({
164
- group: category,
165
- min,
166
- q1,
167
- median,
168
- q3,
169
- max,
170
- mean,
171
- value: mean
172
- });
173
- }
109
+ const stats = categoryMap.get(category);
110
+ const avgVals = stats.map(s => s.avgVal);
111
+ const medVals = stats.map(s => s.medVal);
112
+ const minVals = stats.map(s => s.minVal);
113
+ const maxVals = stats.map(s => s.maxVal);
114
+ const q1Vals = stats.map(s => s.q1Val);
115
+ const q3Vals = stats.map(s => s.q3Val);
116
+ const avg = (arr) => arr.reduce((a, b) => a + b, 0) / arr.length;
117
+ result.push({
118
+ group: category,
119
+ min: Math.min(...minVals),
120
+ q1: avg(q1Vals),
121
+ median: avg(medVals),
122
+ q3: avg(q3Vals),
123
+ max: Math.max(...maxVals),
124
+ mean: avg(avgVals),
125
+ value: avg(avgVals)
126
+ });
174
127
  });
175
128
  this.boxplotData = result;
176
129
  }
@@ -219,7 +172,7 @@ let KpiLevel2Comparison = class KpiLevel2Comparison extends LitElement {
219
172
  }
220
173
  return html `
221
174
  <div class="comparison-container">
222
- <div class="comparison-title">카테고리 비교 분석 (${this.currentMonth})</div>
175
+ <div class="comparison-title">카테고리 비교 분석</div>
223
176
 
224
177
  <div class="charts-section">
225
178
  <div class="chart-card">
@@ -346,6 +299,14 @@ KpiLevel2Comparison.styles = css `
346
299
  color: #666;
347
300
  }
348
301
  `;
302
+ __decorate([
303
+ property({ type: String }),
304
+ __metadata("design:type", String)
305
+ ], KpiLevel2Comparison.prototype, "sectorType", void 0);
306
+ __decorate([
307
+ property({ type: String }),
308
+ __metadata("design:type", String)
309
+ ], KpiLevel2Comparison.prototype, "buildingUsage", void 0);
349
310
  __decorate([
350
311
  state(),
351
312
  __metadata("design:type", Object)
@@ -366,10 +327,6 @@ __decorate([
366
327
  state(),
367
328
  __metadata("design:type", Array)
368
329
  ], KpiLevel2Comparison.prototype, "categories", void 0);
369
- __decorate([
370
- state(),
371
- __metadata("design:type", Object)
372
- ], KpiLevel2Comparison.prototype, "currentMonth", void 0);
373
330
  __decorate([
374
331
  state(),
375
332
  __metadata("design:type", Object)