@dssp/dkpi 1.0.0-alpha.81 → 1.0.0-alpha.82

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.
@@ -9,7 +9,7 @@ export declare class KpiSystemGuide extends PageView {
9
9
  title: string;
10
10
  };
11
11
  firstUpdated(): Promise<void>;
12
- /** 메트릭이 사용되는 KPI 목록 */
12
+ /** 메트릭이 사용되는 KPI 목록 — 비활성 KPI/Y 그룹 제외 */
13
13
  private getMetricUsage;
14
14
  private showDetail;
15
15
  private closeDetail;
@@ -91,12 +91,16 @@ let KpiSystemGuide = class KpiSystemGuide extends PageView {
91
91
  this.loading = false;
92
92
  }
93
93
  }
94
- /** 메트릭이 사용되는 KPI 목록 */
94
+ /** 메트릭이 사용되는 KPI 목록 — 비활성 KPI/Y 그룹 제외 */
95
95
  getMetricUsage(metricName) {
96
96
  var _a;
97
97
  const used = [];
98
98
  for (const y of this.kpiRoot) {
99
+ if (y.active === false)
100
+ continue;
99
101
  for (const x of (y.kpis || [])) {
102
+ if (x.active === false)
103
+ continue;
100
104
  if ((_a = x.formula) === null || _a === void 0 ? void 0 : _a.includes(`[${metricName}]`)) {
101
105
  used.push(x.name);
102
106
  }
@@ -138,11 +142,14 @@ let KpiSystemGuide = class KpiSystemGuide extends PageView {
138
142
  <!-- Z 루트 -->
139
143
  <div class="z-node" @click=${() => this.showDetail({ name: 'Z. 전체스코어', description: 'Y1~Y6 가중합', formula: 'Y1×w1 + Y2×w2 + ... + Y6×w6', scoreType: 'DIRECT', valueType: 'CALCULATED' }, 'kpi')}>
140
144
  <div class="z-name">Z. 전체스코어</div>
141
- <div class="z-formula">= ${this.kpiRoot.map((y) => `${y.name.split('.')[0]}×${y.weight}`).join(' + ')}</div>
145
+ <div class="z-formula">= ${this.kpiRoot
146
+ .filter((y) => y.active !== false)
147
+ .map((y) => `${y.name.split('.')[0]}×${y.weight}`)
148
+ .join(' + ')}</div>
142
149
  </div>
143
150
 
144
- <!-- Y 그룹들 -->
145
- ${this.kpiRoot.map((y) => {
151
+ <!-- Y 그룹들 (비활성 Y 제외) -->
152
+ ${this.kpiRoot.filter((y) => y.active !== false).map((y) => {
146
153
  const activeKpis = (y.kpis || []).filter((x) => x.active !== false);
147
154
  return html `
148
155
  <div class="y-group">
@@ -174,15 +181,17 @@ let KpiSystemGuide = class KpiSystemGuide extends PageView {
174
181
  })}
175
182
  </div>
176
183
 
177
- <!-- 2. 원천 메트릭 -->
178
- <div class="tree-section">
179
- <div class="section-title">📋 원천 메트릭 (${this.metrics.filter((m) => m.active).length}개 정의)</div>
180
- <div class="metrics-grid">
181
- ${this.metrics
182
- .filter((m) => m.active)
183
- .map((m) => {
184
- const usage = this.getMetricUsage(m.name);
184
+ <!-- 2. 원천 메트릭 — active=true 이면서 활성 KPI formula 에 실제 참조되는 것만 -->
185
+ ${(() => {
186
+ const usedMetrics = this.metrics
187
+ .filter((m) => m.active)
188
+ .map((m) => ({ m, usage: this.getMetricUsage(m.name) }))
189
+ .filter(({ usage }) => usage.length > 0);
185
190
  return html `
191
+ <div class="tree-section">
192
+ <div class="section-title">📋 원천 메트릭 (${usedMetrics.length}개 사용)</div>
193
+ <div class="metrics-grid">
194
+ ${usedMetrics.map(({ m, usage }) => html `
186
195
  <div class="metric-card" @click=${() => this.showDetail(m, 'metric')}>
187
196
  <div class="metric-name">${m.name}</div>
188
197
  <div class="metric-unit">
@@ -195,14 +204,13 @@ let KpiSystemGuide = class KpiSystemGuide extends PageView {
195
204
  : m.source
196
205
  : m.collectType || 'MANUAL'}
197
206
  </div>
198
- ${usage.length > 0
199
- ? html `<div class="metric-used">→ ${usage.join(', ')}</div>`
200
- : html `<div class="metric-used" style="color:#ccc;">미사용</div>`}
207
+ <div class="metric-used">→ ${usage.join(', ')}</div>
201
208
  </div>
202
- `;
203
- })}
204
- </div>
205
- </div>
209
+ `)}
210
+ </div>
211
+ </div>
212
+ `;
213
+ })()}
206
214
 
207
215
  <!-- 3. 시스템 요약 -->
208
216
  <div class="tree-section">
@@ -213,11 +221,11 @@ let KpiSystemGuide = class KpiSystemGuide extends PageView {
213
221
  <div style="font-size:0.85rem;color:#555;">X-level 지표</div>
214
222
  </div>
215
223
  <div style="flex:1;min-width:200px;background:#e8f5e9;padding:16px;border-radius:8px;text-align:center;">
216
- <div style="font-size:2rem;font-weight:800;color:#2e7d32;">${this.kpiRoot.length}</div>
224
+ <div style="font-size:2rem;font-weight:800;color:#2e7d32;">${this.kpiRoot.filter((y) => y.active !== false).length}</div>
217
225
  <div style="font-size:0.85rem;color:#555;">Y-level 영역</div>
218
226
  </div>
219
227
  <div style="flex:1;min-width:200px;background:#fff3e0;padding:16px;border-radius:8px;text-align:center;">
220
- <div style="font-size:2rem;font-weight:800;color:#e65100;">${this.metrics.filter((m) => m.active).length}</div>
228
+ <div style="font-size:2rem;font-weight:800;color:#e65100;">${this.metrics.filter((m) => m.active && this.getMetricUsage(m.name).length > 0).length}</div>
221
229
  <div style="font-size:0.85rem;color:#555;">원천 메트릭</div>
222
230
  </div>
223
231
  <div style="flex:1;min-width:200px;background:#fce4ec;padding:16px;border-radius:8px;text-align:center;">
@@ -1 +1 @@
1
- {"version":3,"file":"kpi-system-guide.js","sourceRoot":"","sources":["../../../client/pages/kpi-admin/kpi-system-guide.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, css, nothing } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\nimport { PageView } from '@operato/shell'\nimport { ScrollbarStyles } from '@operato/styles'\nimport { client } from '@operato/graphql'\nimport { navigate } from '@operato/shell'\nimport gql from 'graphql-tag'\n\n/**\n * KPI 시스템 종합 안내 페이지\n *\n * DB에서 실시간으로 KPI 계층, 산식, 메트릭, scoreType/valueType 등을 조회하여\n * 시스템 전체 구조를 한눈에 파악할 수 있는 매뉴얼 겸 라이브 대시보드.\n */\n\nconst GET_KPI_SYSTEM = gql`\n query {\n kpiRoot: kpisLevel1 {\n id\n name\n description\n formula\n weight\n scoreType\n valueType\n active\n kpis: children {\n id\n name\n description\n formula\n weight\n scoreType\n valueType\n active\n grades\n }\n }\n kpiMetrics {\n items {\n id\n name\n unit\n source\n collectType\n active\n }\n }\n }\n`\n\nconst SCORE_TYPE_LABELS: Record<string, { label: string; color: string }> = {\n DIRECT: { label: 'DIRECT (변환 없음)', color: '#888' },\n FORMULA: { label: 'FORMULA (수식 변환)', color: '#9c27b0' },\n LOOKUP: { label: 'LOOKUP (등급표)', color: '#1976d2' },\n CUSTOM: { label: 'CUSTOM (2D 룩업)', color: '#e65100' }\n}\n\n/** 외부 시스템 URL 매핑 */\nconst EXTERNAL_SYSTEM_URLS: Record<string, string> = {\n '세움터(EAIS)': 'https://cloud.eais.go.kr',\n '키스콘(KISCON)': 'https://www.kiscon.net',\n '올바로(Allbaro)': 'https://www.allbaro.or.kr',\n '전자카드제': 'https://www.cw.or.kr'\n}\n\nconst VALUE_TYPE_LABELS: Record<string, { label: string; color: string; icon: string }> = {\n MEASURED: { label: '외부 수집', color: '#1976d2', icon: '📡' },\n ASSESSED: { label: '감리자 평가', color: '#2e7d32', icon: '✍️' },\n CALCULATED: { label: '산식 계산', color: '#9c27b0', icon: '🔢' },\n COMPOSITE: { label: '복합 입력', color: '#e65100', icon: '🔀' }\n}\n\n@customElement('kpi-system-guide')\nexport class KpiSystemGuide extends PageView {\n static styles = [\n ScrollbarStyles,\n css`\n :host {\n display: block;\n overflow-y: auto;\n background: #f5f6fa;\n padding: 24px;\n }\n\n .page-title {\n font-size: 1.5rem;\n font-weight: 800;\n color: #1a237e;\n margin-bottom: 4px;\n }\n .page-subtitle {\n font-size: 0.9rem;\n color: #666;\n margin-bottom: 24px;\n }\n\n /* 계층 트리 */\n .tree-section {\n background: #fff;\n border-radius: 12px;\n padding: 24px;\n margin-bottom: 24px;\n box-shadow: 0 2px 8px rgba(0,0,0,0.06);\n }\n .section-title {\n font-size: 1.1rem;\n font-weight: 700;\n color: #333;\n margin-bottom: 16px;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n /* Z 루트 */\n .z-node {\n background: linear-gradient(135deg, #1a237e, #283593);\n color: #fff;\n border-radius: 10px;\n padding: 16px 20px;\n margin-bottom: 16px;\n cursor: pointer;\n }\n .z-node:hover { opacity: 0.95; }\n .z-name { font-size: 1.1rem; font-weight: 700; }\n .z-formula { font-size: 0.8rem; opacity: 0.8; margin-top: 4px; font-family: monospace; }\n\n /* Y 그룹 */\n .y-group {\n margin-bottom: 16px;\n border: 1px solid #e0e0e0;\n border-radius: 10px;\n overflow: hidden;\n }\n .y-header {\n background: linear-gradient(135deg, #0c4da2, #1565c0);\n color: #fff;\n padding: 12px 16px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n cursor: pointer;\n }\n .y-header:hover { opacity: 0.95; }\n .y-name { font-weight: 700; font-size: 1rem; }\n .y-weight {\n background: rgba(255,255,255,0.2);\n padding: 2px 10px;\n border-radius: 12px;\n font-size: 0.8rem;\n }\n .y-body { padding: 12px; }\n .y-formula {\n font-size: 0.8rem;\n color: #555;\n background: #f8f9fa;\n padding: 8px 12px;\n border-radius: 6px;\n font-family: monospace;\n margin-bottom: 10px;\n word-break: break-all;\n }\n\n /* X 카드 */\n .x-cards { display: flex; flex-wrap: wrap; gap: 8px; }\n .x-card {\n flex: 1 1 220px;\n background: #fff;\n border: 1px solid #e8e8e8;\n border-radius: 8px;\n padding: 10px 14px;\n cursor: pointer;\n transition: all 0.2s;\n min-width: 200px;\n }\n .x-card:hover {\n border-color: #1976d2;\n box-shadow: 0 2px 8px rgba(25,118,210,0.15);\n }\n .x-name { font-weight: 600; font-size: 0.85rem; color: #333; margin-bottom: 4px; }\n .x-formula {\n font-size: 0.75rem; color: #888; font-family: monospace;\n white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n margin-bottom: 6px;\n }\n .x-badges { display: flex; gap: 4px; flex-wrap: wrap; }\n .badge {\n font-size: 0.65rem;\n padding: 2px 6px;\n border-radius: 4px;\n font-weight: 600;\n }\n .badge-score { background: #e3f2fd; color: #1565c0; }\n .badge-value { background: #e8f5e9; color: #2e7d32; }\n\n /* 메트릭 섹션 */\n .metrics-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));\n gap: 10px;\n }\n .metric-card {\n background: #fff;\n border: 1px solid #e8e8e8;\n border-radius: 8px;\n padding: 10px 14px;\n transition: all 0.2s;\n }\n .metric-card:hover { border-color: #4caf50; }\n .metric-name { font-weight: 600; font-size: 0.85rem; color: #333; }\n .metric-unit { font-size: 0.75rem; color: #888; }\n .metric-used {\n font-size: 0.7rem; color: #1976d2; margin-top: 4px;\n }\n\n /* 팝업 */\n .popup-overlay {\n position: fixed; top: 0; left: 0; right: 0; bottom: 0;\n background: rgba(0,0,0,0.3); z-index: 1000;\n display: flex; align-items: center; justify-content: center;\n }\n .popup-box {\n background: #fff; border-radius: 12px; padding: 24px;\n min-width: 400px; max-width: 600px; max-height: 80vh;\n overflow-y: auto; box-shadow: 0 8px 32px rgba(0,0,0,0.2);\n }\n .popup-title {\n font-size: 1.1rem; font-weight: 700; color: #1a237e;\n margin-bottom: 16px; display: flex; justify-content: space-between;\n }\n .popup-close {\n background: none; border: none; font-size: 1.2rem;\n cursor: pointer; color: #999;\n }\n .popup-row {\n display: flex; gap: 8px; margin-bottom: 8px;\n font-size: 0.85rem;\n }\n .popup-label { color: #888; min-width: 100px; flex-shrink: 0; }\n .popup-value { color: #333; font-weight: 500; }\n .popup-formula {\n background: #f5f5f5; padding: 10px; border-radius: 6px;\n font-family: monospace; font-size: 0.85rem; margin: 8px 0;\n word-break: break-all;\n }\n .popup-link {\n color: #1976d2; cursor: pointer; text-decoration: underline;\n font-size: 0.85rem;\n }\n .popup-link:hover { color: #0d47a1; }\n\n /* 범례 */\n .legend {\n display: flex; gap: 16px; flex-wrap: wrap;\n margin-bottom: 20px; padding: 12px 16px;\n background: #fff; border-radius: 8px;\n box-shadow: 0 1px 4px rgba(0,0,0,0.04);\n }\n .legend-group { display: flex; align-items: center; gap: 4px; font-size: 0.8rem; }\n .legend-title { font-weight: 700; color: #555; margin-right: 4px; }\n `\n ]\n\n @state() kpiRoot: any[] = []\n @state() metrics: any[] = []\n @state() loading = true\n @state() selectedDetail: any = null\n\n get context() {\n return { title: 'KPI 시스템 가이드' }\n }\n\n async firstUpdated() {\n try {\n const { data } = await client.query({ query: GET_KPI_SYSTEM })\n this.kpiRoot = data.kpiRoot || []\n this.metrics = data.kpiMetrics?.items || []\n } catch (e) {\n console.error('Failed to load KPI system data:', e)\n } finally {\n this.loading = false\n }\n }\n\n /** 메트릭이 사용되는 KPI 목록 */\n private getMetricUsage(metricName: string): string[] {\n const used: string[] = []\n for (const y of this.kpiRoot) {\n for (const x of (y.kpis || [])) {\n if (x.formula?.includes(`[${metricName}]`)) {\n used.push(x.name)\n }\n }\n }\n return used\n }\n\n private showDetail(item: any, type: 'kpi' | 'metric') {\n this.selectedDetail = { ...item, _type: type }\n }\n\n private closeDetail() {\n this.selectedDetail = null\n }\n\n render() {\n if (this.loading) return html`<div style=\"padding:40px;text-align:center;color:#888;\">로딩 중...</div>`\n\n // Z 노드 (루트의 부모)\n const allX = this.kpiRoot.flatMap((y: any) => (y.kpis || []).filter((x: any) => x.active !== false))\n\n return html`\n <div class=\"page-title\">KPI 시스템 가이드</div>\n <div class=\"page-subtitle\">\n 건축현장 성과평가 KPI 체계의 전체 구조, 산식, 메트릭 연관관계를 확인합니다.\n 모든 정보는 DB에서 실시간 조회됩니다.\n </div>\n\n <!-- 범례 -->\n <div class=\"legend\">\n <span class=\"legend-title\">Score 산정:</span>\n ${Object.entries(SCORE_TYPE_LABELS).map(([, v]) =>\n html`<span class=\"legend-group\"><span class=\"badge badge-score\">${v.label}</span></span>`\n )}\n <span style=\"width:16px\"></span>\n <span class=\"legend-title\">Value 획득:</span>\n ${Object.entries(VALUE_TYPE_LABELS).map(([, v]) =>\n html`<span class=\"legend-group\">${v.icon} <span class=\"badge badge-value\">${v.label}</span></span>`\n )}\n </div>\n\n <!-- 1. KPI 계층 구조 -->\n <div class=\"tree-section\">\n <div class=\"section-title\">📊 KPI 계층 구조 (Z → Y → X)</div>\n\n <!-- Z 루트 -->\n <div class=\"z-node\" @click=${() => this.showDetail({ name: 'Z. 전체스코어', description: 'Y1~Y6 가중합', formula: 'Y1×w1 + Y2×w2 + ... + Y6×w6', scoreType: 'DIRECT', valueType: 'CALCULATED' }, 'kpi')}>\n <div class=\"z-name\">Z. 전체스코어</div>\n <div class=\"z-formula\">= ${this.kpiRoot.map((y: any) => `${y.name.split('.')[0]}×${y.weight}`).join(' + ')}</div>\n </div>\n\n <!-- Y 그룹들 -->\n ${this.kpiRoot.map((y: any) => {\n const activeKpis = (y.kpis || []).filter((x: any) => x.active !== false)\n return html`\n <div class=\"y-group\">\n <div class=\"y-header\" @click=${() => this.showDetail(y, 'kpi')}>\n <span class=\"y-name\">${y.name}</span>\n <span class=\"y-weight\">가중치: ${y.weight}</span>\n </div>\n <div class=\"y-body\">\n ${y.formula ? html`<div class=\"y-formula\">${y.formula}</div>` : ''}\n <div class=\"x-cards\">\n ${activeKpis.map((x: any) => {\n const st = SCORE_TYPE_LABELS[x.scoreType] || { label: x.scoreType || '미설정', color: '#999' }\n const vt = VALUE_TYPE_LABELS[x.valueType] || { label: x.valueType || '미설정', color: '#999', icon: '?' }\n return html`\n <div class=\"x-card\" @click=${() => this.showDetail(x, 'kpi')}>\n <div class=\"x-name\">${x.name}</div>\n <div class=\"x-formula\">${x.formula || '—'}</div>\n <div class=\"x-badges\">\n <span class=\"badge badge-score\">${st.label}</span>\n <span class=\"badge badge-value\">${vt.icon} ${vt.label}</span>\n </div>\n </div>\n `\n })}\n </div>\n </div>\n </div>\n `\n })}\n </div>\n\n <!-- 2. 원천 메트릭 -->\n <div class=\"tree-section\">\n <div class=\"section-title\">📋 원천 메트릭 (${this.metrics.filter((m: any) => m.active).length}개 정의)</div>\n <div class=\"metrics-grid\">\n ${this.metrics\n .filter((m: any) => m.active)\n .map((m: any) => {\n const usage = this.getMetricUsage(m.name)\n return html`\n <div class=\"metric-card\" @click=${() => this.showDetail(m, 'metric')}>\n <div class=\"metric-name\">${m.name}</div>\n <div class=\"metric-unit\">\n ${m.unit || '—'} ·\n ${m.collectType === 'EXTERNAL' && m.source\n ? EXTERNAL_SYSTEM_URLS[m.source]\n ? html`<a href=\"${EXTERNAL_SYSTEM_URLS[m.source]}\" target=\"_blank\" rel=\"noopener\"\n style=\"color:#1976d2;text-decoration:none;\" @click=${(e: Event) => e.stopPropagation()}\n >${m.source} ↗</a>`\n : m.source\n : m.collectType || 'MANUAL'}\n </div>\n ${usage.length > 0\n ? html`<div class=\"metric-used\">→ ${usage.join(', ')}</div>`\n : html`<div class=\"metric-used\" style=\"color:#ccc;\">미사용</div>`}\n </div>\n `\n })}\n </div>\n </div>\n\n <!-- 3. 시스템 요약 -->\n <div class=\"tree-section\">\n <div class=\"section-title\">📈 시스템 요약</div>\n <div style=\"display:flex;gap:16px;flex-wrap:wrap;\">\n <div style=\"flex:1;min-width:200px;background:#e3f2fd;padding:16px;border-radius:8px;text-align:center;\">\n <div style=\"font-size:2rem;font-weight:800;color:#1565c0;\">${allX.length}</div>\n <div style=\"font-size:0.85rem;color:#555;\">X-level 지표</div>\n </div>\n <div style=\"flex:1;min-width:200px;background:#e8f5e9;padding:16px;border-radius:8px;text-align:center;\">\n <div style=\"font-size:2rem;font-weight:800;color:#2e7d32;\">${this.kpiRoot.length}</div>\n <div style=\"font-size:0.85rem;color:#555;\">Y-level 영역</div>\n </div>\n <div style=\"flex:1;min-width:200px;background:#fff3e0;padding:16px;border-radius:8px;text-align:center;\">\n <div style=\"font-size:2rem;font-weight:800;color:#e65100;\">${this.metrics.filter((m: any) => m.active).length}</div>\n <div style=\"font-size:0.85rem;color:#555;\">원천 메트릭</div>\n </div>\n <div style=\"flex:1;min-width:200px;background:#fce4ec;padding:16px;border-radius:8px;text-align:center;\">\n <div style=\"font-size:2rem;font-weight:800;color:#c62828;\">${allX.filter((x: any) => x.valueType === 'ASSESSED').length}</div>\n <div style=\"font-size:0.85rem;color:#555;\">감리자 평가 항목</div>\n </div>\n </div>\n </div>\n\n <!-- 팝업 -->\n ${this.selectedDetail ? this._renderDetailPopup() : nothing}\n `\n }\n\n private _renderDetailPopup() {\n const d = this.selectedDetail\n const isKpi = d._type === 'kpi'\n\n return html`\n <div class=\"popup-overlay\" @click=${() => this.closeDetail()}>\n <div class=\"popup-box\" @click=${(e: Event) => e.stopPropagation()}>\n <div class=\"popup-title\">\n <span>${d.name}</span>\n <button class=\"popup-close\" @click=${() => this.closeDetail()}>×</button>\n </div>\n\n ${d.description ? html`\n <div class=\"popup-row\">\n <span class=\"popup-label\">설명</span>\n <span class=\"popup-value\">${d.description}</span>\n </div>\n ` : ''}\n\n ${isKpi ? html`\n ${d.formula ? html`\n <div class=\"popup-row\"><span class=\"popup-label\">산식</span></div>\n <div class=\"popup-formula\">${d.formula}</div>\n ` : ''}\n\n ${d.scoreType ? html`\n <div class=\"popup-row\">\n <span class=\"popup-label\">Score 산정</span>\n <span class=\"popup-value\" style=\"color:${(SCORE_TYPE_LABELS[d.scoreType] || {}).color || '#333'}\">\n ${(SCORE_TYPE_LABELS[d.scoreType] || {}).label || d.scoreType}\n </span>\n </div>\n ` : ''}\n\n ${d.valueType ? html`\n <div class=\"popup-row\">\n <span class=\"popup-label\">Value 획득</span>\n <span class=\"popup-value\" style=\"color:${(VALUE_TYPE_LABELS[d.valueType] || {}).color || '#333'}\">\n ${(VALUE_TYPE_LABELS[d.valueType] || {}).icon || ''} ${(VALUE_TYPE_LABELS[d.valueType] || {}).label || d.valueType}\n </span>\n </div>\n ` : ''}\n\n ${d.weight ? html`\n <div class=\"popup-row\">\n <span class=\"popup-label\">가중치</span>\n <span class=\"popup-value\">${d.weight}</span>\n </div>\n ` : ''}\n\n <div style=\"margin-top:16px;padding-top:12px;border-top:1px solid #eee;\">\n <span class=\"popup-link\" @click=${() => { this.closeDetail(); navigate('kpi-overview') }}>\n → KPI 개요에서 보기\n </span>\n &nbsp;&nbsp;\n <span class=\"popup-link\" @click=${() => { this.closeDetail(); navigate('kpi-admin') }}>\n → KPI 관리에서 편집\n </span>\n </div>\n ` : html`\n <div class=\"popup-row\">\n <span class=\"popup-label\">단위</span>\n <span class=\"popup-value\">${d.unit || '—'}</span>\n </div>\n <div class=\"popup-row\">\n <span class=\"popup-label\">수집 방식</span>\n <span class=\"popup-value\">${d.collectType || 'MANUAL'}</span>\n </div>\n <div class=\"popup-row\">\n <span class=\"popup-label\">데이터 소스</span>\n <span class=\"popup-value\">\n ${d.source && EXTERNAL_SYSTEM_URLS[d.source]\n ? html`<a href=\"${EXTERNAL_SYSTEM_URLS[d.source]}\" target=\"_blank\" rel=\"noopener\" style=\"color:#1976d2;\">${d.source} ↗</a>`\n : d.source || '미설정'}\n </span>\n </div>\n\n <div style=\"margin-top:12px;\">\n <div class=\"popup-label\" style=\"margin-bottom:6px;\">사용처:</div>\n ${this.getMetricUsage(d.name).map(kpi =>\n html`<div style=\"font-size:0.85rem;color:#1976d2;margin-bottom:2px;\">· ${kpi}</div>`\n )}\n ${this.getMetricUsage(d.name).length === 0 ? html`<div style=\"color:#ccc;font-size:0.85rem;\">미사용</div>` : ''}\n </div>\n\n <div style=\"margin-top:16px;padding-top:12px;border-top:1px solid #eee;\">\n <span class=\"popup-link\" @click=${() => { this.closeDetail(); navigate('kpi-metric-value-list') }}>\n → 메트릭 값 관리\n </span>\n </div>\n `}\n </div>\n </div>\n `\n }\n}\n"]}
1
+ {"version":3,"file":"kpi-system-guide.js","sourceRoot":"","sources":["../../../client/pages/kpi-admin/kpi-system-guide.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, css, nothing } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\nimport { PageView } from '@operato/shell'\nimport { ScrollbarStyles } from '@operato/styles'\nimport { client } from '@operato/graphql'\nimport { navigate } from '@operato/shell'\nimport gql from 'graphql-tag'\n\n/**\n * KPI 시스템 종합 안내 페이지\n *\n * DB에서 실시간으로 KPI 계층, 산식, 메트릭, scoreType/valueType 등을 조회하여\n * 시스템 전체 구조를 한눈에 파악할 수 있는 매뉴얼 겸 라이브 대시보드.\n */\n\nconst GET_KPI_SYSTEM = gql`\n query {\n kpiRoot: kpisLevel1 {\n id\n name\n description\n formula\n weight\n scoreType\n valueType\n active\n kpis: children {\n id\n name\n description\n formula\n weight\n scoreType\n valueType\n active\n grades\n }\n }\n kpiMetrics {\n items {\n id\n name\n unit\n source\n collectType\n active\n }\n }\n }\n`\n\nconst SCORE_TYPE_LABELS: Record<string, { label: string; color: string }> = {\n DIRECT: { label: 'DIRECT (변환 없음)', color: '#888' },\n FORMULA: { label: 'FORMULA (수식 변환)', color: '#9c27b0' },\n LOOKUP: { label: 'LOOKUP (등급표)', color: '#1976d2' },\n CUSTOM: { label: 'CUSTOM (2D 룩업)', color: '#e65100' }\n}\n\n/** 외부 시스템 URL 매핑 */\nconst EXTERNAL_SYSTEM_URLS: Record<string, string> = {\n '세움터(EAIS)': 'https://cloud.eais.go.kr',\n '키스콘(KISCON)': 'https://www.kiscon.net',\n '올바로(Allbaro)': 'https://www.allbaro.or.kr',\n '전자카드제': 'https://www.cw.or.kr'\n}\n\nconst VALUE_TYPE_LABELS: Record<string, { label: string; color: string; icon: string }> = {\n MEASURED: { label: '외부 수집', color: '#1976d2', icon: '📡' },\n ASSESSED: { label: '감리자 평가', color: '#2e7d32', icon: '✍️' },\n CALCULATED: { label: '산식 계산', color: '#9c27b0', icon: '🔢' },\n COMPOSITE: { label: '복합 입력', color: '#e65100', icon: '🔀' }\n}\n\n@customElement('kpi-system-guide')\nexport class KpiSystemGuide extends PageView {\n static styles = [\n ScrollbarStyles,\n css`\n :host {\n display: block;\n overflow-y: auto;\n background: #f5f6fa;\n padding: 24px;\n }\n\n .page-title {\n font-size: 1.5rem;\n font-weight: 800;\n color: #1a237e;\n margin-bottom: 4px;\n }\n .page-subtitle {\n font-size: 0.9rem;\n color: #666;\n margin-bottom: 24px;\n }\n\n /* 계층 트리 */\n .tree-section {\n background: #fff;\n border-radius: 12px;\n padding: 24px;\n margin-bottom: 24px;\n box-shadow: 0 2px 8px rgba(0,0,0,0.06);\n }\n .section-title {\n font-size: 1.1rem;\n font-weight: 700;\n color: #333;\n margin-bottom: 16px;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n /* Z 루트 */\n .z-node {\n background: linear-gradient(135deg, #1a237e, #283593);\n color: #fff;\n border-radius: 10px;\n padding: 16px 20px;\n margin-bottom: 16px;\n cursor: pointer;\n }\n .z-node:hover { opacity: 0.95; }\n .z-name { font-size: 1.1rem; font-weight: 700; }\n .z-formula { font-size: 0.8rem; opacity: 0.8; margin-top: 4px; font-family: monospace; }\n\n /* Y 그룹 */\n .y-group {\n margin-bottom: 16px;\n border: 1px solid #e0e0e0;\n border-radius: 10px;\n overflow: hidden;\n }\n .y-header {\n background: linear-gradient(135deg, #0c4da2, #1565c0);\n color: #fff;\n padding: 12px 16px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n cursor: pointer;\n }\n .y-header:hover { opacity: 0.95; }\n .y-name { font-weight: 700; font-size: 1rem; }\n .y-weight {\n background: rgba(255,255,255,0.2);\n padding: 2px 10px;\n border-radius: 12px;\n font-size: 0.8rem;\n }\n .y-body { padding: 12px; }\n .y-formula {\n font-size: 0.8rem;\n color: #555;\n background: #f8f9fa;\n padding: 8px 12px;\n border-radius: 6px;\n font-family: monospace;\n margin-bottom: 10px;\n word-break: break-all;\n }\n\n /* X 카드 */\n .x-cards { display: flex; flex-wrap: wrap; gap: 8px; }\n .x-card {\n flex: 1 1 220px;\n background: #fff;\n border: 1px solid #e8e8e8;\n border-radius: 8px;\n padding: 10px 14px;\n cursor: pointer;\n transition: all 0.2s;\n min-width: 200px;\n }\n .x-card:hover {\n border-color: #1976d2;\n box-shadow: 0 2px 8px rgba(25,118,210,0.15);\n }\n .x-name { font-weight: 600; font-size: 0.85rem; color: #333; margin-bottom: 4px; }\n .x-formula {\n font-size: 0.75rem; color: #888; font-family: monospace;\n white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n margin-bottom: 6px;\n }\n .x-badges { display: flex; gap: 4px; flex-wrap: wrap; }\n .badge {\n font-size: 0.65rem;\n padding: 2px 6px;\n border-radius: 4px;\n font-weight: 600;\n }\n .badge-score { background: #e3f2fd; color: #1565c0; }\n .badge-value { background: #e8f5e9; color: #2e7d32; }\n\n /* 메트릭 섹션 */\n .metrics-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));\n gap: 10px;\n }\n .metric-card {\n background: #fff;\n border: 1px solid #e8e8e8;\n border-radius: 8px;\n padding: 10px 14px;\n transition: all 0.2s;\n }\n .metric-card:hover { border-color: #4caf50; }\n .metric-name { font-weight: 600; font-size: 0.85rem; color: #333; }\n .metric-unit { font-size: 0.75rem; color: #888; }\n .metric-used {\n font-size: 0.7rem; color: #1976d2; margin-top: 4px;\n }\n\n /* 팝업 */\n .popup-overlay {\n position: fixed; top: 0; left: 0; right: 0; bottom: 0;\n background: rgba(0,0,0,0.3); z-index: 1000;\n display: flex; align-items: center; justify-content: center;\n }\n .popup-box {\n background: #fff; border-radius: 12px; padding: 24px;\n min-width: 400px; max-width: 600px; max-height: 80vh;\n overflow-y: auto; box-shadow: 0 8px 32px rgba(0,0,0,0.2);\n }\n .popup-title {\n font-size: 1.1rem; font-weight: 700; color: #1a237e;\n margin-bottom: 16px; display: flex; justify-content: space-between;\n }\n .popup-close {\n background: none; border: none; font-size: 1.2rem;\n cursor: pointer; color: #999;\n }\n .popup-row {\n display: flex; gap: 8px; margin-bottom: 8px;\n font-size: 0.85rem;\n }\n .popup-label { color: #888; min-width: 100px; flex-shrink: 0; }\n .popup-value { color: #333; font-weight: 500; }\n .popup-formula {\n background: #f5f5f5; padding: 10px; border-radius: 6px;\n font-family: monospace; font-size: 0.85rem; margin: 8px 0;\n word-break: break-all;\n }\n .popup-link {\n color: #1976d2; cursor: pointer; text-decoration: underline;\n font-size: 0.85rem;\n }\n .popup-link:hover { color: #0d47a1; }\n\n /* 범례 */\n .legend {\n display: flex; gap: 16px; flex-wrap: wrap;\n margin-bottom: 20px; padding: 12px 16px;\n background: #fff; border-radius: 8px;\n box-shadow: 0 1px 4px rgba(0,0,0,0.04);\n }\n .legend-group { display: flex; align-items: center; gap: 4px; font-size: 0.8rem; }\n .legend-title { font-weight: 700; color: #555; margin-right: 4px; }\n `\n ]\n\n @state() kpiRoot: any[] = []\n @state() metrics: any[] = []\n @state() loading = true\n @state() selectedDetail: any = null\n\n get context() {\n return { title: 'KPI 시스템 가이드' }\n }\n\n async firstUpdated() {\n try {\n const { data } = await client.query({ query: GET_KPI_SYSTEM })\n this.kpiRoot = data.kpiRoot || []\n this.metrics = data.kpiMetrics?.items || []\n } catch (e) {\n console.error('Failed to load KPI system data:', e)\n } finally {\n this.loading = false\n }\n }\n\n /** 메트릭이 사용되는 KPI 목록 — 비활성 KPI/Y 그룹 제외 */\n private getMetricUsage(metricName: string): string[] {\n const used: string[] = []\n for (const y of this.kpiRoot) {\n if (y.active === false) continue\n for (const x of (y.kpis || [])) {\n if (x.active === false) continue\n if (x.formula?.includes(`[${metricName}]`)) {\n used.push(x.name)\n }\n }\n }\n return used\n }\n\n private showDetail(item: any, type: 'kpi' | 'metric') {\n this.selectedDetail = { ...item, _type: type }\n }\n\n private closeDetail() {\n this.selectedDetail = null\n }\n\n render() {\n if (this.loading) return html`<div style=\"padding:40px;text-align:center;color:#888;\">로딩 중...</div>`\n\n // Z 노드 (루트의 부모)\n const allX = this.kpiRoot.flatMap((y: any) => (y.kpis || []).filter((x: any) => x.active !== false))\n\n return html`\n <div class=\"page-title\">KPI 시스템 가이드</div>\n <div class=\"page-subtitle\">\n 건축현장 성과평가 KPI 체계의 전체 구조, 산식, 메트릭 연관관계를 확인합니다.\n 모든 정보는 DB에서 실시간 조회됩니다.\n </div>\n\n <!-- 범례 -->\n <div class=\"legend\">\n <span class=\"legend-title\">Score 산정:</span>\n ${Object.entries(SCORE_TYPE_LABELS).map(([, v]) =>\n html`<span class=\"legend-group\"><span class=\"badge badge-score\">${v.label}</span></span>`\n )}\n <span style=\"width:16px\"></span>\n <span class=\"legend-title\">Value 획득:</span>\n ${Object.entries(VALUE_TYPE_LABELS).map(([, v]) =>\n html`<span class=\"legend-group\">${v.icon} <span class=\"badge badge-value\">${v.label}</span></span>`\n )}\n </div>\n\n <!-- 1. KPI 계층 구조 -->\n <div class=\"tree-section\">\n <div class=\"section-title\">📊 KPI 계층 구조 (Z → Y → X)</div>\n\n <!-- Z 루트 -->\n <div class=\"z-node\" @click=${() => this.showDetail({ name: 'Z. 전체스코어', description: 'Y1~Y6 가중합', formula: 'Y1×w1 + Y2×w2 + ... + Y6×w6', scoreType: 'DIRECT', valueType: 'CALCULATED' }, 'kpi')}>\n <div class=\"z-name\">Z. 전체스코어</div>\n <div class=\"z-formula\">= ${this.kpiRoot\n .filter((y: any) => y.active !== false)\n .map((y: any) => `${y.name.split('.')[0]}×${y.weight}`)\n .join(' + ')}</div>\n </div>\n\n <!-- Y 그룹들 (비활성 Y 제외) -->\n ${this.kpiRoot.filter((y: any) => y.active !== false).map((y: any) => {\n const activeKpis = (y.kpis || []).filter((x: any) => x.active !== false)\n return html`\n <div class=\"y-group\">\n <div class=\"y-header\" @click=${() => this.showDetail(y, 'kpi')}>\n <span class=\"y-name\">${y.name}</span>\n <span class=\"y-weight\">가중치: ${y.weight}</span>\n </div>\n <div class=\"y-body\">\n ${y.formula ? html`<div class=\"y-formula\">${y.formula}</div>` : ''}\n <div class=\"x-cards\">\n ${activeKpis.map((x: any) => {\n const st = SCORE_TYPE_LABELS[x.scoreType] || { label: x.scoreType || '미설정', color: '#999' }\n const vt = VALUE_TYPE_LABELS[x.valueType] || { label: x.valueType || '미설정', color: '#999', icon: '?' }\n return html`\n <div class=\"x-card\" @click=${() => this.showDetail(x, 'kpi')}>\n <div class=\"x-name\">${x.name}</div>\n <div class=\"x-formula\">${x.formula || '—'}</div>\n <div class=\"x-badges\">\n <span class=\"badge badge-score\">${st.label}</span>\n <span class=\"badge badge-value\">${vt.icon} ${vt.label}</span>\n </div>\n </div>\n `\n })}\n </div>\n </div>\n </div>\n `\n })}\n </div>\n\n <!-- 2. 원천 메트릭 — active=true 이면서 활성 KPI formula 에 실제 참조되는 것만 -->\n ${(() => {\n const usedMetrics = this.metrics\n .filter((m: any) => m.active)\n .map((m: any) => ({ m, usage: this.getMetricUsage(m.name) }))\n .filter(({ usage }: any) => usage.length > 0)\n return html`\n <div class=\"tree-section\">\n <div class=\"section-title\">📋 원천 메트릭 (${usedMetrics.length}개 사용)</div>\n <div class=\"metrics-grid\">\n ${usedMetrics.map(({ m, usage }: any) => html`\n <div class=\"metric-card\" @click=${() => this.showDetail(m, 'metric')}>\n <div class=\"metric-name\">${m.name}</div>\n <div class=\"metric-unit\">\n ${m.unit || '—'} ·\n ${m.collectType === 'EXTERNAL' && m.source\n ? EXTERNAL_SYSTEM_URLS[m.source]\n ? html`<a href=\"${EXTERNAL_SYSTEM_URLS[m.source]}\" target=\"_blank\" rel=\"noopener\"\n style=\"color:#1976d2;text-decoration:none;\" @click=${(e: Event) => e.stopPropagation()}\n >${m.source} ↗</a>`\n : m.source\n : m.collectType || 'MANUAL'}\n </div>\n <div class=\"metric-used\">→ ${usage.join(', ')}</div>\n </div>\n `)}\n </div>\n </div>\n `\n })()}\n\n <!-- 3. 시스템 요약 -->\n <div class=\"tree-section\">\n <div class=\"section-title\">📈 시스템 요약</div>\n <div style=\"display:flex;gap:16px;flex-wrap:wrap;\">\n <div style=\"flex:1;min-width:200px;background:#e3f2fd;padding:16px;border-radius:8px;text-align:center;\">\n <div style=\"font-size:2rem;font-weight:800;color:#1565c0;\">${allX.length}</div>\n <div style=\"font-size:0.85rem;color:#555;\">X-level 지표</div>\n </div>\n <div style=\"flex:1;min-width:200px;background:#e8f5e9;padding:16px;border-radius:8px;text-align:center;\">\n <div style=\"font-size:2rem;font-weight:800;color:#2e7d32;\">${this.kpiRoot.filter((y: any) => y.active !== false).length}</div>\n <div style=\"font-size:0.85rem;color:#555;\">Y-level 영역</div>\n </div>\n <div style=\"flex:1;min-width:200px;background:#fff3e0;padding:16px;border-radius:8px;text-align:center;\">\n <div style=\"font-size:2rem;font-weight:800;color:#e65100;\">${this.metrics.filter((m: any) => m.active && this.getMetricUsage(m.name).length > 0).length}</div>\n <div style=\"font-size:0.85rem;color:#555;\">원천 메트릭</div>\n </div>\n <div style=\"flex:1;min-width:200px;background:#fce4ec;padding:16px;border-radius:8px;text-align:center;\">\n <div style=\"font-size:2rem;font-weight:800;color:#c62828;\">${allX.filter((x: any) => x.valueType === 'ASSESSED').length}</div>\n <div style=\"font-size:0.85rem;color:#555;\">감리자 평가 항목</div>\n </div>\n </div>\n </div>\n\n <!-- 팝업 -->\n ${this.selectedDetail ? this._renderDetailPopup() : nothing}\n `\n }\n\n private _renderDetailPopup() {\n const d = this.selectedDetail\n const isKpi = d._type === 'kpi'\n\n return html`\n <div class=\"popup-overlay\" @click=${() => this.closeDetail()}>\n <div class=\"popup-box\" @click=${(e: Event) => e.stopPropagation()}>\n <div class=\"popup-title\">\n <span>${d.name}</span>\n <button class=\"popup-close\" @click=${() => this.closeDetail()}>×</button>\n </div>\n\n ${d.description ? html`\n <div class=\"popup-row\">\n <span class=\"popup-label\">설명</span>\n <span class=\"popup-value\">${d.description}</span>\n </div>\n ` : ''}\n\n ${isKpi ? html`\n ${d.formula ? html`\n <div class=\"popup-row\"><span class=\"popup-label\">산식</span></div>\n <div class=\"popup-formula\">${d.formula}</div>\n ` : ''}\n\n ${d.scoreType ? html`\n <div class=\"popup-row\">\n <span class=\"popup-label\">Score 산정</span>\n <span class=\"popup-value\" style=\"color:${(SCORE_TYPE_LABELS[d.scoreType] || {}).color || '#333'}\">\n ${(SCORE_TYPE_LABELS[d.scoreType] || {}).label || d.scoreType}\n </span>\n </div>\n ` : ''}\n\n ${d.valueType ? html`\n <div class=\"popup-row\">\n <span class=\"popup-label\">Value 획득</span>\n <span class=\"popup-value\" style=\"color:${(VALUE_TYPE_LABELS[d.valueType] || {}).color || '#333'}\">\n ${(VALUE_TYPE_LABELS[d.valueType] || {}).icon || ''} ${(VALUE_TYPE_LABELS[d.valueType] || {}).label || d.valueType}\n </span>\n </div>\n ` : ''}\n\n ${d.weight ? html`\n <div class=\"popup-row\">\n <span class=\"popup-label\">가중치</span>\n <span class=\"popup-value\">${d.weight}</span>\n </div>\n ` : ''}\n\n <div style=\"margin-top:16px;padding-top:12px;border-top:1px solid #eee;\">\n <span class=\"popup-link\" @click=${() => { this.closeDetail(); navigate('kpi-overview') }}>\n → KPI 개요에서 보기\n </span>\n &nbsp;&nbsp;\n <span class=\"popup-link\" @click=${() => { this.closeDetail(); navigate('kpi-admin') }}>\n → KPI 관리에서 편집\n </span>\n </div>\n ` : html`\n <div class=\"popup-row\">\n <span class=\"popup-label\">단위</span>\n <span class=\"popup-value\">${d.unit || '—'}</span>\n </div>\n <div class=\"popup-row\">\n <span class=\"popup-label\">수집 방식</span>\n <span class=\"popup-value\">${d.collectType || 'MANUAL'}</span>\n </div>\n <div class=\"popup-row\">\n <span class=\"popup-label\">데이터 소스</span>\n <span class=\"popup-value\">\n ${d.source && EXTERNAL_SYSTEM_URLS[d.source]\n ? html`<a href=\"${EXTERNAL_SYSTEM_URLS[d.source]}\" target=\"_blank\" rel=\"noopener\" style=\"color:#1976d2;\">${d.source} ↗</a>`\n : d.source || '미설정'}\n </span>\n </div>\n\n <div style=\"margin-top:12px;\">\n <div class=\"popup-label\" style=\"margin-bottom:6px;\">사용처:</div>\n ${this.getMetricUsage(d.name).map(kpi =>\n html`<div style=\"font-size:0.85rem;color:#1976d2;margin-bottom:2px;\">· ${kpi}</div>`\n )}\n ${this.getMetricUsage(d.name).length === 0 ? html`<div style=\"color:#ccc;font-size:0.85rem;\">미사용</div>` : ''}\n </div>\n\n <div style=\"margin-top:16px;padding-top:12px;border-top:1px solid #eee;\">\n <span class=\"popup-link\" @click=${() => { this.closeDetail(); navigate('kpi-metric-value-list') }}>\n → 메트릭 값 관리\n </span>\n </div>\n `}\n </div>\n </div>\n `\n }\n}\n"]}
@@ -47,6 +47,11 @@ export declare class SvProjectCompleteTab1Plan extends LitElement {
47
47
  connectedCallback(): Promise<void>;
48
48
  willUpdate(changedProperties: Map<string, any>): void;
49
49
  private _getInitData;
50
+ /**
51
+ * 공사비/공사기간 의 "계획" 컬럼 input 편집 핸들러 — collectedPlan 표시값 동기화 +
52
+ * 해당 KpiMetric 의 value 도 upsert 하여 _save 시 patches 에 포함되도록.
53
+ */
54
+ private _onPlanInputChange;
50
55
  private _onInputChange;
51
56
  /**
52
57
  * metric.periodType 에 따라 적절한 row 를 upsert.
@@ -124,7 +124,12 @@ let SvProjectCompleteTab1Plan = SvProjectCompleteTab1Plan_1 = class SvProjectCom
124
124
  : ''}>• ${metric.name}</div>
125
125
  <div class="cell ${planFilled ? 'just-filled' : ''}">
126
126
  ${isDisplayInput
127
- ? html `<input .value=${shownPlan} disabled /> ${unit}
127
+ ? html `<input
128
+ numeric
129
+ .value=${shownPlan !== null && shownPlan !== void 0 ? shownPlan : 0}
130
+ @input=${(e) => this._onPlanInputChange(e, metric, projectKey)}
131
+ />
132
+ ${unit}
128
133
  ${planSrc ? html `<span class="src-chip">🔗 ${planSrc}</span>` : ''}`
129
134
  : html `-`}
130
135
  </div>
@@ -133,9 +138,10 @@ let SvProjectCompleteTab1Plan = SvProjectCompleteTab1Plan_1 = class SvProjectCom
133
138
  ${unit}
134
139
  ${src ? html `<span class="src-chip">🔗 ${src}</span>` : ''}
135
140
  </div>
136
- <div class="unit ${isDisplayInput ? effDiffClass : diffClass}">
137
- ${isDisplayInput ? effDiffSign : diffSign}
138
- ${Math.abs(isDisplayInput ? effDiff : diffValue).toLocaleString()} ${unit}
141
+ <div class="unit ${isDisplayInput ? effDiffClass : ''}">
142
+ ${isDisplayInput
143
+ ? html `${effDiffSign} ${Math.abs(effDiff).toLocaleString()} ${unit}`
144
+ : ''}
139
145
  </div>
140
146
  </div>`;
141
147
  })}
@@ -281,10 +287,14 @@ let SvProjectCompleteTab1Plan = SvProjectCompleteTab1Plan_1 = class SvProjectCom
281
287
  if (!mapping)
282
288
  continue;
283
289
  if (isPlan) {
284
- // 계획값 (키스콘) — 계획 칼럼에 반영
290
+ // 계획값 (키스콘 제공) — 계획 칼럼 표시 + KpiMetric value 에도 upsert
291
+ // 하여 _save 시 patches 에 포함. 사용자가 계획 input 직접 편집해도 같은 흐름.
285
292
  this.collectedPlan = Object.assign(Object.assign({}, this.collectedPlan), { [projectKey]: numericValue });
286
293
  this.planSources = Object.assign(Object.assign({}, this.planSources), { [projectKey]: r.label });
287
294
  this.justFilled = Object.assign(Object.assign({}, this.justFilled), { [`plan:${projectKey}`]: true });
295
+ const planMetric = this.kpiMetrics.find((m) => { var _a; return ((_a = KPI_METRIC_KEY_MAPPING.find(x => x.label === m.name)) === null || _a === void 0 ? void 0 : _a.projectKey) === projectKey; });
296
+ if (planMetric)
297
+ this._setMetricValue(planMetric, numericValue);
288
298
  }
289
299
  else {
290
300
  // 실제값 (세움터/올바로/기타) — 매칭되는 metric 의 실제 칼럼에 반영
@@ -359,10 +369,22 @@ let SvProjectCompleteTab1Plan = SvProjectCompleteTab1Plan_1 = class SvProjectCom
359
369
  }
360
370
  async _getInitData() {
361
371
  var _a, _b;
372
+ // getKpiMetrics 가 이미 active=true 만 반환. 여기선 평가 (Step2) 만 추가 제외.
373
+ // 비활성화는 KPI 관리 admin 에서 active=false 처리.
362
374
  const kpiMetrics = await getKpiMetrics((_a = this.project) === null || _a === void 0 ? void 0 : _a.code);
363
- this.kpiMetrics = kpiMetrics.filter(item => !item.name.includes('평가')) || []; // 평가 텍스트가 들어간건 제외
375
+ this.kpiMetrics = kpiMetrics.filter(item => !item.name.includes('평가')) || [];
364
376
  this.kpiMetricValues = await getKpiMetricValues(this.project.id, (_b = this.project) === null || _b === void 0 ? void 0 : _b.code);
365
377
  }
378
+ /**
379
+ * 공사비/공사기간 의 "계획" 컬럼 input 편집 핸들러 — collectedPlan 표시값 동기화 +
380
+ * 해당 KpiMetric 의 value 도 upsert 하여 _save 시 patches 에 포함되도록.
381
+ */
382
+ _onPlanInputChange(event, metric, projectKey) {
383
+ const target = event.target;
384
+ const inputVal = Number(target.value.replace(/[^\d.]/g, ''));
385
+ this.collectedPlan = Object.assign(Object.assign({}, this.collectedPlan), { [projectKey]: inputVal });
386
+ this.kpiMetricValues = this._upsertValueForMetric(this.kpiMetricValues, metric, inputVal);
387
+ }
366
388
  // Input 요소의 값이 변경될 때 호출되는 콜백 함수
367
389
  _onInputChange(event, metric) {
368
390
  const target = event.target;
@@ -1 +1 @@
1
- {"version":3,"file":"pc-tab1-plan.js","sourceRoot":"","sources":["../../../client/pages/project-complete-tabs/pc-tab1-plan.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, LitElement } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { calcDiff, calcDateDiff } from '../../shared/func'\nimport {\n getKpiMetricValues,\n getKpiMetrics,\n updateProjectCompleteStep1,\n collectProjectExternalData\n} from '../../shared/complete-api'\nimport { INTEGRATION_SOURCES } from '../../shared/integration-fetch'\nimport moment from 'moment-timezone'\nimport { notify } from '@operato/layout'\nimport { hasPrivilege } from '@things-factory/auth-base/dist-client'\n\nconst KPI_METRIC_KEY_MAPPING = [\n { label: '공사기간', projectKey: 'constructionPeriod' },\n { label: '총 근로자수', projectKey: 'totalWorkerCount' },\n { label: '연간 근로자 수', projectKey: 'annualWorkerCount' },\n { label: '투입인력', projectKey: 'workerCount' },\n { label: '재해 건수', projectKey: 'accidentCount' },\n { label: '일정 이탈 수준', projectKey: 'scheduleDeviationLevel' },\n { label: '총 건설 폐기물 발생량', projectKey: 'totalConstructionWasteAmount' },\n { label: '용적율', projectKey: 'floorAreaRatio' },\n { label: '총 설계 변경 건', projectKey: 'designChangeCount' },\n { label: '공사비', projectKey: 'constructionCost' },\n { label: '연면적', projectKey: 'area' },\n { label: '지상층수', projectKey: 'upperFloorCount' },\n { label: '지하층수', projectKey: 'lowerFloorCount' }\n]\n// 계획값이 없는 것들 (실제값으로 표시할 필드)\nconst NO_PLAN_FIELDS = ['workerCount', 'area', 'floorAreaRatio', 'designChangeCount', 'upperFloorCount', 'lowerFloorCount']\n\n@customElement('sv-pc-tab1-plan')\nexport class SvProjectCompleteTab1Plan extends LitElement {\n static styles = [\n css`\n :host {\n display: block;\n }\n .title {\n color: #212529;\n font-size: 13px;\n font-weight: 400;\n line-height: 24px;\n text-align: center;\n }\n\n .rows {\n display: flex;\n flex-direction: column;\n padding: 8px 6px;\n }\n .row.header {\n min-height: 35px;\n background: #f3f3fa;\n border-top: 2px #0c4da2 solid;\n grid-template-columns: 220px 1fr 1fr 200px;\n padding: 0px 25px;\n\n .header-label {\n color: #212529;\n text-align: center;\n }\n }\n .row {\n display: grid;\n grid-template-columns: 220px 1fr 1fr 200px;\n gap: 6px 10px;\n align-items: center;\n padding: 8px 25px;\n border-bottom: 1px rgba(0, 0, 0, 0.1) solid;\n\n .cell {\n display: flex;\n align-items: center;\n justify-content: center;\n }\n }\n .label {\n color: #35618e;\n font-size: 16px;\n letter-spacing: -0.05em;\n white-space: nowrap;\n display: flex;\n justify-content: center;\n }\n input {\n padding: 6px 8px;\n border: 1px solid rgba(0, 0, 0, 0.1);\n border-radius: 5px;\n background: #ffffff;\n color: #212529;\n font-size: 16px;\n margin-right: 5px;\n\n &:disabled {\n background: #f6f6f6;\n }\n }\n .unit {\n text-align: center;\n color: #212529;\n }\n .plus {\n color: #e13232;\n font-weight: 700;\n }\n .minus {\n color: #1e88e5;\n font-weight: 700;\n }\n .button-line {\n display: flex;\n justify-content: center;\n gap: 10px;\n margin-top: 16px;\n }\n .ghost-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 10px;\n background: #35618e;\n color: #ffffff;\n border-radius: 5px;\n cursor: pointer;\n }\n .ghost-btn.secondary {\n background: #24be7b;\n }\n .ghost-btn.disabled,\n .collect-btn.disabled {\n opacity: 0.45;\n cursor: not-allowed;\n }\n\n /* ── 자동 수집 패널 ── */\n .collect-panel {\n margin: 4px 6px 14px;\n border: 1px solid #d7e3f2;\n border-radius: 8px;\n background: linear-gradient(180deg, #f7fafe 0%, #ffffff 100%);\n overflow: hidden;\n }\n .collect-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 16px;\n background: #eaf2fb;\n border-bottom: 1px solid #d7e3f2;\n }\n .collect-head .ttl {\n color: #0c4da2;\n font-weight: 700;\n font-size: 14px;\n letter-spacing: -0.03em;\n }\n .collect-head .ttl small {\n color: #5a7da6;\n font-weight: 400;\n margin-left: 8px;\n }\n .collect-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 7px 14px;\n background: #0c4da2;\n color: #fff;\n border-radius: 6px;\n cursor: pointer;\n font-size: 13px;\n font-weight: 600;\n }\n .collect-btn[disabled] {\n background: #9bb4d2;\n cursor: default;\n }\n .source-list {\n display: flex;\n gap: 10px;\n padding: 12px 16px;\n flex-wrap: wrap;\n }\n .source-card {\n flex: 1 1 200px;\n border: 1px solid #e3e9f0;\n border-radius: 7px;\n padding: 10px 12px;\n background: #fff;\n transition: border-color 0.2s, box-shadow 0.2s;\n }\n .source-card.collecting {\n border-color: #2e79be;\n box-shadow: 0 0 0 3px rgba(46, 121, 190, 0.12);\n }\n .source-card.done {\n border-color: #24be7b;\n }\n .source-card .sc-head {\n display: flex;\n align-items: center;\n gap: 8px;\n font-weight: 600;\n color: #212529;\n font-size: 13px;\n }\n .source-card .sc-head .sys-icon {\n font-size: 16px;\n }\n .source-card .sc-status {\n margin-left: auto;\n font-size: 12px;\n }\n .sc-status.idle {\n color: #aab4c0;\n }\n .sc-status.collecting {\n color: #2e79be;\n }\n .sc-status.done {\n color: #24be7b;\n font-weight: 700;\n }\n .sc-status.error {\n color: #8a8a8a;\n font-weight: 600;\n }\n .source-card.error {\n background: #fafafa;\n border-color: #e0e0e0;\n }\n .source-card .sc-error-msg {\n margin-top: 6px;\n padding: 6px 8px;\n border-radius: 4px;\n background: #f3f4f6;\n color: #6b7280;\n font-size: 12px;\n line-height: 1.4;\n word-break: break-word;\n }\n .source-card .sc-fields {\n margin-top: 8px;\n display: flex;\n flex-direction: column;\n gap: 3px;\n }\n .sc-field {\n font-size: 12px;\n color: #5a6b7b;\n display: flex;\n justify-content: space-between;\n }\n .sc-field b {\n color: #0c4da2;\n }\n .sc-desc {\n font-size: 11px;\n color: #9aa7b4;\n margin-top: 2px;\n }\n /* 가져온 값 출처 칩 */\n .src-chip {\n display: inline-flex;\n align-items: center;\n margin-left: 6px;\n padding: 1px 7px;\n font-size: 11px;\n border-radius: 10px;\n background: #e7f3ec;\n color: #1d9c63;\n white-space: nowrap;\n }\n .cell.just-filled input {\n animation: flash 1.1s ease;\n }\n /* 미입력 — kpiMetricValue 가 비어있는 metric 행 */\n .label.pending::before {\n content: '⚠';\n color: #e74c3c;\n margin-right: 4px;\n }\n .cell.pending input {\n border-color: #e74c3c;\n background-color: #fff5f5;\n }\n @keyframes flash {\n 0% {\n background: #fff7cc;\n }\n 100% {\n background: #ffffff;\n }\n }\n `\n ]\n\n @state() kpiMetricValues: any = []\n @state() kpiMetrics: any = []\n @property({ type: Object }) project: any = {}\n\n /** 연동 진행 상태: source → 'idle' | 'collecting' | 'done' */\n @state() collectStatus: Record<string, 'idle' | 'collecting' | 'done' | 'error'> = {}\n @state() collectError: Record<string, string> = {}\n /** 자동 수집한 실제값의 출처: metricId → 시스템 라벨 */\n @state() valueSources: Record<string, string> = {}\n /** 자동 수집한 계획값: projectKey → 값 (키스콘이 제공하는 계획공사기간/계획공사비) */\n @state() collectedPlan: Record<string, number> = {}\n /** 계획값 출처: projectKey → 시스템 라벨 */\n @state() planSources: Record<string, string> = {}\n /** 시스템별 수집 요약 (카드 표시용): 라벨 → [{label, text}] */\n @state() collectedSummary: Record<string, { label: string; text: string }[]> = {}\n /** 방금 채워진 셀 강조용 (metricId 또는 plan:projectKey) */\n @state() justFilled: Record<string, boolean> = {}\n @state() collecting = false\n\n /** kpi:input — Step1 계획값 입력/저장 권한 */\n @state() canSave = false\n /** kpi:auto-collect — 외부 시스템 자동 수집 권한 */\n @state() canAutoCollect = false\n\n /** 계획 칼럼을 갖는 항목 (키스콘이 계획값을 제공) */\n private static readonly PLAN_KEYS = ['constructionPeriod', 'constructionCost']\n\n render() {\n // 전월 (last month) YYYY-MM. 월별 metric 의 \"전월 데이터 입력\" 검사 기준.\n const lastMonth = moment().tz('Asia/Seoul').subtract(1, 'month').format('YYYY-MM')\n\n return html`\n <div class=\"title\">\n <div>\n 당초 계획 대비 실제 진행 과정에서 변동된 공사비, 공기(공사기간), 면적, 기타 주요 항목을 현실에 맞게 수정·입력합니다.\n <br />이 정보는 성과 분석, KPI 평가, 통계 산출 등에 기준값으로 사용되므로, 가능한 한 실제 값 기준으로 정확히\n 입력해주시기 바랍니다.\n </div>\n </div>\n\n ${this._renderCollectPanel()}\n\n <div class=\"rows\">\n <div class=\"row header\">\n <div class=\"header-label\">기본정보</div>\n <div class=\"header-label\">계획</div>\n <div class=\"header-label\">실제</div>\n <div class=\"header-label\">편차</div>\n </div>\n\n ${this.kpiMetrics.map(metric => {\n // 상수로 정의된 매핑 정보에서 metric.name으로 projectKey를 찾음\n const projectKey = KPI_METRIC_KEY_MAPPING.find(item => item.label === metric.name)?.projectKey || ''\n const isMonthly = metric.periodType === 'MONTH'\n // 현재값 lookup:\n // MONTH → 전월 row (단일 진실원)\n // ALLTIME → 단일 ALLTIME row\n // 그 외 → metricId+periodType 매칭\n const kpiMetricValue = isMonthly\n ? this.kpiMetricValues.find(\n (item: any) =>\n item.metricId === metric.id &&\n item.periodType === 'MONTH' &&\n (item.valueDate || '').startsWith(lastMonth)\n ) || {}\n : this.kpiMetricValues.find(\n (item: any) => item.metricId === metric.id && item.periodType === metric.periodType\n ) || {}\n const isDisplayInput = projectKey === 'constructionPeriod' || projectKey === 'constructionCost' // 유효한 계획 필드\n\n // planValue는 project에서 찾고 없으면 buildingComplex의 값에서 찾음\n const basePlanValue = this.project[projectKey] || this.project?.buildingComplex?.[projectKey] || 0\n let planValue = basePlanValue\n const unit = kpiMetricValue.unit || metric.unit || ''\n\n // 2. 계획 값 처리\n // 공사기간은 기간을 일 단위로 계산\n if (projectKey === 'constructionPeriod') {\n planValue = Math.ceil(calcDateDiff(this.project.startDate, this.project.endDate) / 30)\n } else if (!isDisplayInput) {\n planValue = 0\n }\n\n // 1. 실제 값 처리 kpi값을 찾고, 없으면 (NO_PLAN_FIELDS에 해당하는 필드는 원래 계획값 사용)\n const actualValue = kpiMetricValue.value ?? (NO_PLAN_FIELDS.includes(projectKey) ? basePlanValue : 0)\n\n const diffValue = calcDiff(planValue, actualValue)\n const diffClass = diffValue === 0 ? '' : diffValue > 0 ? 'plus' : 'minus'\n const diffSign = diffValue === 0 ? '' : diffValue > 0 ? '+' : '-'\n\n const src = this.valueSources[metric.id]\n // 계획값: 키스콘 수집값이 있으면 그것을, 없으면 계산된 planValue\n const planSrc = this.planSources[projectKey]\n const shownPlan = this.collectedPlan[projectKey] ?? planValue\n const planFilled = this.justFilled[`plan:${projectKey}`]\n // 계획값이 키스콘에서 들어왔으면 편차도 그 기준으로 재계산\n const effDiff = calcDiff(shownPlan, actualValue)\n const effDiffClass = effDiff === 0 ? '' : effDiff > 0 ? 'plus' : 'minus'\n const effDiffSign = effDiff === 0 ? '' : effDiff > 0 ? '+' : '-'\n\n // periodType 무관하게 값 없으면 pending. 메시지는 MONTH 만 \"전월 데이터 입력 필요\".\n const isPending = kpiMetricValue.value === undefined || kpiMetricValue.value === null\n\n return html`<div class=\"row\">\n <div class=\"label ${isPending ? 'pending' : ''}\"\n title=${isPending\n ? isMonthly\n ? '전월 데이터 입력 필요'\n : '아직 입력되지 않은 항목'\n : ''}>• ${metric.name}</div>\n <div class=\"cell ${planFilled ? 'just-filled' : ''}\">\n ${isDisplayInput\n ? html`<input .value=${shownPlan} disabled /> ${unit}\n ${planSrc ? html`<span class=\"src-chip\">🔗 ${planSrc}</span>` : ''}`\n : html`-`}\n </div>\n <div class=\"cell ${this.justFilled[metric.id] ? 'just-filled' : ''} ${isPending ? 'pending' : ''}\">\n <input numeric .value=${actualValue ?? 0} @input=${(e: InputEvent) => this._onInputChange(e, metric)} />\n ${unit}\n ${src ? html`<span class=\"src-chip\">🔗 ${src}</span>` : ''}\n </div>\n <div class=\"unit ${isDisplayInput ? effDiffClass : diffClass}\">\n ${isDisplayInput ? effDiffSign : diffSign}\n ${Math.abs(isDisplayInput ? effDiff : diffValue).toLocaleString()} ${unit}\n </div>\n </div>`\n })}\n\n <div class=\"button-line\">\n <div class=\"ghost-btn\" @click=${this._reset}>초기화</div>\n <div\n class=\"ghost-btn secondary ${this.canSave ? '' : 'disabled'}\"\n title=${this.canSave ? '' : 'kpi:input 권한 필요'}\n @click=${() => this.canSave && this._save()}\n >\n 저장\n </div>\n </div>\n </div>\n `\n }\n\n private _renderCollectPanel() {\n return html`\n <div class=\"collect-panel\">\n <div class=\"collect-head\">\n <div class=\"ttl\">시스템 연동 자동 수집 <small>외부 시스템에서 기본정보를 가져옵니다</small></div>\n <div\n class=\"collect-btn ${this.canAutoCollect ? '' : 'disabled'}\"\n ?disabled=${this.collecting}\n title=${this.canAutoCollect ? '' : 'kpi:auto-collect 권한 필요'}\n @click=${() =>\n this.canAutoCollect && !this.collecting ? this._autoCollect() : null}\n >\n ${this.collecting ? '수집 중…' : '⟳ 자동 수집'}\n </div>\n </div>\n <div class=\"source-list\">\n ${INTEGRATION_SOURCES.map(meta => {\n const status = this.collectStatus[meta.source] || 'idle'\n const errMsg = this.collectError[meta.source]\n // 카드 안의 수집 필드 summary — 실제/계획 무관 collectedSummary 기준으로 통일.\n // (valueSources 만 보면 키스콘처럼 계획값만 채우는 시스템은 표시 안 됨)\n const collectedFields = this._collectedFieldsOf(meta.label)\n const statusText =\n status === 'collecting'\n ? '연동 중…'\n : status === 'done'\n ? '완료 ✓'\n : status === 'error'\n ? '정보 없음 ⓘ'\n : '대기'\n return html`\n <div class=\"source-card ${status}\">\n <div class=\"sc-head\">\n <span class=\"sys-icon\">${meta.icon}</span>\n <span>${meta.label}</span>\n <span class=\"sc-status ${status}\">${statusText}</span>\n </div>\n <div class=\"sc-desc\">${meta.desc}</div>\n ${status === 'error' && errMsg ? html`<div class=\"sc-error-msg\">${errMsg}</div>` : ''}\n ${status === 'done' && collectedFields.length\n ? html`<div class=\"sc-fields\">\n ${collectedFields.map(\n f => html`<div class=\"sc-field\"><span>${f.label}</span><b>${f.text}</b></div>`\n )}\n </div>`\n : ''}\n </div>\n `\n })}\n </div>\n </div>\n `\n }\n\n /** 패널 카드에 표시할, 해당 시스템에서 수집한 값 요약 */\n private _collectedFieldsOf(sourceLabel: string): { label: string; text: string }[] {\n return this.collectedSummary[sourceLabel] || []\n }\n\n /** 자동 수집 — 백엔드의 collectProjectExternalData mutation 1번 호출.\n * 서버가 시나리오 3종 (세움터/올바로/키스콘) 을 순차 실행하고 시스템별 ok/메시지를 반환.\n * 성공/실패와 무관하게 시나리오 step 이 KPI Value 를 DB 에 적재하므로 호출 후\n * KPI Metric Values 를 재조회해 form 을 자동 갱신.\n *\n * 운영 현실: 키스콘·올바로는 시공사 자격증명 미확보가 많아 오류 빈도 높음.\n * 세움터는 공공데이터 서비스키만으로 동작 가능. 시스템별로 카드에 독립 표시.\n */\n private async _autoCollect() {\n if (!this.project?.id) {\n notify({ message: '프로젝트 정보가 없습니다.' })\n return\n }\n this.collecting = true\n this.collectStatus = {}\n this.collectError = {}\n this.collectedSummary = {}\n this.collectedPlan = {}\n this.planSources = {}\n this.valueSources = {}\n\n // 모든 카드를 collecting 상태로 표시\n const collecting: Record<string, 'collecting'> = {}\n for (const meta of INTEGRATION_SOURCES) collecting[meta.source] = 'collecting'\n this.collectStatus = collecting\n\n try {\n const results = await collectProjectExternalData(this.project.id)\n\n // 시스템별 status / error 메시지 업데이트 + result data 를 form 에 매핑\n const status: Record<string, 'done' | 'error'> = {}\n const errors: Record<string, string> = {}\n for (const r of results) {\n status[r.source] = r.ok ? 'done' : 'error'\n if (!r.ok) {\n errors[r.source] = r.message\n continue\n }\n // 시나리오 result 객체 = { projectKey: value, ... }.\n // KPI_METRIC_KEY_MAPPING 매칭 키 → form 채움. 미매칭 키 (siteType,\n // structureType 등 프로젝트/BuildingComplex 기본 속성) → 카드 요약만.\n const summary: { label: string; text: string }[] = []\n // KPI_METRIC_KEY_MAPPING 에 없는 키들의 한글 라벨. BuildingComplex/Project 직접 속성용.\n const NON_KPI_LABEL: Record<string, string> = {\n siteType: '건축현장유형',\n structureType: '구조형태',\n coverageRatio: '건폐율',\n householdCount: '세대수',\n buildingCount: '동수',\n startDate: '착공일',\n endDate: '준공일',\n permitDate: '건축허가일',\n bldNm: '건물명'\n }\n for (const [projectKey, rawValue] of Object.entries(r.data || {})) {\n if (rawValue === null || rawValue === undefined || rawValue === '') continue\n // 비-primitive (객체/배열) 값은 시나리오 측 return 형태 오류 — form 매핑/표시\n // 모두 skip 하고 console 에 진단 로그만 남김. 흔한 원인은 시나리오 마지막 step 이\n // `return { result: { ... } }` 처럼 wrap 한 케이스.\n if (typeof rawValue === 'object') {\n console.warn(`[자동수집] '${r.label}'.${projectKey} 가 object — 시나리오 return 형태 점검`, rawValue)\n continue\n }\n const mapping = KPI_METRIC_KEY_MAPPING.find(x => x.projectKey === projectKey)\n const label = mapping?.label || NON_KPI_LABEL[projectKey] || projectKey\n const isPlan = SvProjectCompleteTab1Plan.PLAN_KEYS.includes(projectKey)\n\n // summary 카드 표시용 — string 값은 그대로, number 는 toLocaleString\n const numericValue = typeof rawValue === 'number' ? rawValue : Number(rawValue)\n const isNumeric = Number.isFinite(numericValue) && typeof rawValue !== 'string'\n const valueText = isNumeric ? numericValue.toLocaleString() : String(rawValue)\n summary.push({ label, text: valueText })\n\n // KPI 매핑이 없는 키 (siteType/structureType 등) 는 form 채움 skip — 단지 노출만.\n if (!mapping) continue\n\n if (isPlan) {\n // 계획값 (키스콘) — 계획 칼럼에 반영\n this.collectedPlan = { ...this.collectedPlan, [projectKey]: numericValue }\n this.planSources = { ...this.planSources, [projectKey]: r.label }\n this.justFilled = { ...this.justFilled, [`plan:${projectKey}`]: true }\n } else {\n // 실제값 (세움터/올바로/기타) — 매칭되는 metric 의 실제 칼럼에 반영\n const metric = this.kpiMetrics.find(\n (m: any) => KPI_METRIC_KEY_MAPPING.find(x => x.label === m.name)?.projectKey === projectKey\n )\n if (!metric) continue\n this._setMetricValue(metric, numericValue)\n this.valueSources = { ...this.valueSources, [metric.id]: r.label }\n this.justFilled = { ...this.justFilled, [metric.id]: true }\n }\n }\n this.collectedSummary = { ...this.collectedSummary, [r.label]: summary }\n }\n this.collectStatus = status\n this.collectError = errors\n\n // 재조회 안 함 — 시나리오는 fetch + result 반환만 책임 (DB 적재 X). 직전에\n // _setMetricValue 가 memory 에 채운 값들을 DB 의 옛 값으로 덮어쓰면 자동수집\n // 결과가 폼에 반영되지 않음. 사용자가 [저장] 버튼 누르면 그제서야 DB 반영.\n this.requestUpdate()\n setTimeout(() => (this.justFilled = {}), 1300)\n\n const okCount = results.filter(r => r.ok).length\n if (okCount === results.length) {\n notify({ message: `외부 시스템 ${okCount}건 모두 수집 완료. 검토 후 [저장]을 눌러주세요.` })\n } else if (okCount === 0) {\n notify({ message: '외부 시스템에서 가져올 정보가 없습니다.' })\n } else {\n notify({ message: `외부 시스템 수집 — ${okCount}건 완료, 나머지는 정보 없음.` })\n }\n } catch (e) {\n // mutation 자체 실패 (네트워크/권한 등) — 모든 카드를 정보 없음 으로\n const msg = e instanceof Error ? e.message : String(e)\n console.warn('[자동수집] 전체 호출 실패', e)\n const status: Record<string, 'error'> = {}\n const errors: Record<string, string> = {}\n for (const meta of INTEGRATION_SOURCES) {\n status[meta.source] = 'error'\n errors[meta.source] = '업데이트할 정보가 없습니다'\n }\n this.collectStatus = status\n this.collectError = errors\n notify({ message: '외부 시스템 연동이 일시적으로 안 됩니다.' })\n } finally {\n this.collecting = false\n }\n }\n\n /**\n * 메트릭의 실제값 set/add — _onInputChange 와 동일하게 periodType 별 row upsert.\n */\n private _setMetricValue(metric: any, value: number | string) {\n this.kpiMetricValues = this._upsertValueForMetric(this.kpiMetricValues, metric, value)\n }\n\n async connectedCallback() {\n super.connectedCallback()\n const [canSave, canAutoCollect] = await Promise.all([\n hasPrivilege({ category: 'kpi', privilege: 'input', domainOwnerGranted: true, superUserGranted: true }),\n hasPrivilege({ category: 'kpi', privilege: 'auto-collect', domainOwnerGranted: true, superUserGranted: true })\n ])\n this.canSave = canSave\n this.canAutoCollect = canAutoCollect\n }\n\n willUpdate(changedProperties: Map<string, any>) {\n super.willUpdate(changedProperties)\n\n // project가 변경되고, project.id가 존재하면 데이터 로드\n if (changedProperties.has('project') && this.project?.id) {\n this._getInitData()\n }\n }\n\n private async _getInitData() {\n const kpiMetrics = await getKpiMetrics(this.project?.code)\n this.kpiMetrics = kpiMetrics.filter(item => !item.name.includes('평가')) || [] // 평가 텍스트가 들어간건 제외\n this.kpiMetricValues = await getKpiMetricValues(this.project.id, this.project?.code)\n }\n\n // Input 요소의 값이 변경될 때 호출되는 콜백 함수\n private _onInputChange(event: InputEvent, metric: any) {\n const target = event.target as HTMLInputElement\n let inputVal: any = target.value\n\n // 숫자 타입은 다른 문자 입력 제거\n if (target.hasAttribute('numeric')) {\n inputVal = Number(inputVal.replace(/[^\\d.]/g, ''))\n }\n\n this.kpiMetricValues = this._upsertValueForMetric(this.kpiMetricValues, metric, inputVal)\n }\n\n /**\n * metric.periodType 에 따라 적절한 row 를 upsert.\n * - MONTH : 전월 row (YYYY-MM-01). \"오늘 입력 = 지난달 데이터\" 운영 원칙.\n * - ALLTIME : 단일 row (valueDate sentinel 무관).\n * - 그 외 (DAY/WEEK/QUARTER/YEAR/RANGE) : metric.periodType 그대로, valueDate=today.\n * 매칭은 (metricId, periodType) 기준 — MONTH 만 추가로 valueDate prefix.\n */\n private _upsertValueForMetric(values: any[], metric: any, value: any): any[] {\n const today = moment().tz('Asia/Seoul')\n const todayYmd = today.format('YYYY-MM-DD')\n const periodType = metric.periodType\n let updated = [...values]\n\n if (periodType === 'MONTH') {\n const lastMonth = today.clone().subtract(1, 'month')\n const lastMonth1 = lastMonth.format('YYYY-MM-01')\n const lastMonthYm = lastMonth.format('YYYY-MM')\n const idx = updated.findIndex(\n (i: any) =>\n i.metricId === metric.id &&\n i.periodType === 'MONTH' &&\n (i.valueDate || '').startsWith(lastMonthYm)\n )\n if (idx !== -1) {\n updated[idx] = { ...updated[idx], value }\n } else {\n updated.push({\n id: crypto.randomUUID(),\n value,\n metricId: metric.id,\n unit: metric.unit || '',\n org: this.project.id,\n periodType: 'MONTH',\n valueDate: lastMonth1\n })\n }\n } else {\n // ALLTIME / DAY / WEEK / QUARTER / YEAR / RANGE — metric.periodType 그대로 단일 row upsert.\n const idx = updated.findIndex(\n (i: any) => i.metricId === metric.id && i.periodType === periodType\n )\n if (idx !== -1) {\n updated[idx] = { ...updated[idx], value }\n } else {\n updated.push({\n id: crypto.randomUUID(),\n value,\n metricId: metric.id,\n unit: metric.unit || '',\n org: this.project.id,\n periodType,\n valueDate: todayYmd\n })\n }\n }\n\n return updated\n }\n\n private async _save() {\n // 사용자가 실제 편집한 row 만 저장. 자동 산정 metric (계획/실제 공사비·공기) 는\n // 별도의 데이터 출처(project 정보, 키스콘 수집, 다른 metric 의 전월 row 등)에서\n // 도출돼야 하므로 이 화면에서 하드코딩 derive 하지 않음.\n const response = await updateProjectCompleteStep1(this.kpiMetricValues, this.project?.code)\n if (!response.errors) {\n notify({ message: '저장되었습니다.' })\n }\n }\n\n private _reset() {\n this._getInitData()\n }\n}\n"]}
1
+ {"version":3,"file":"pc-tab1-plan.js","sourceRoot":"","sources":["../../../client/pages/project-complete-tabs/pc-tab1-plan.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, LitElement } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { calcDiff, calcDateDiff } from '../../shared/func'\nimport {\n getKpiMetricValues,\n getKpiMetrics,\n updateProjectCompleteStep1,\n collectProjectExternalData\n} from '../../shared/complete-api'\nimport { INTEGRATION_SOURCES } from '../../shared/integration-fetch'\nimport moment from 'moment-timezone'\nimport { notify } from '@operato/layout'\nimport { hasPrivilege } from '@things-factory/auth-base/dist-client'\n\nconst KPI_METRIC_KEY_MAPPING = [\n { label: '공사기간', projectKey: 'constructionPeriod' },\n { label: '총 근로자수', projectKey: 'totalWorkerCount' },\n { label: '연간 근로자 수', projectKey: 'annualWorkerCount' },\n { label: '투입인력', projectKey: 'workerCount' },\n { label: '재해 건수', projectKey: 'accidentCount' },\n { label: '일정 이탈 수준', projectKey: 'scheduleDeviationLevel' },\n { label: '총 건설 폐기물 발생량', projectKey: 'totalConstructionWasteAmount' },\n { label: '용적율', projectKey: 'floorAreaRatio' },\n { label: '총 설계 변경 건', projectKey: 'designChangeCount' },\n { label: '공사비', projectKey: 'constructionCost' },\n { label: '연면적', projectKey: 'area' },\n { label: '지상층수', projectKey: 'upperFloorCount' },\n { label: '지하층수', projectKey: 'lowerFloorCount' }\n]\n// 계획값이 없는 것들 (실제값으로 표시할 필드)\nconst NO_PLAN_FIELDS = ['workerCount', 'area', 'floorAreaRatio', 'designChangeCount', 'upperFloorCount', 'lowerFloorCount']\n\n@customElement('sv-pc-tab1-plan')\nexport class SvProjectCompleteTab1Plan extends LitElement {\n static styles = [\n css`\n :host {\n display: block;\n }\n .title {\n color: #212529;\n font-size: 13px;\n font-weight: 400;\n line-height: 24px;\n text-align: center;\n }\n\n .rows {\n display: flex;\n flex-direction: column;\n padding: 8px 6px;\n }\n .row.header {\n min-height: 35px;\n background: #f3f3fa;\n border-top: 2px #0c4da2 solid;\n grid-template-columns: 220px 1fr 1fr 200px;\n padding: 0px 25px;\n\n .header-label {\n color: #212529;\n text-align: center;\n }\n }\n .row {\n display: grid;\n grid-template-columns: 220px 1fr 1fr 200px;\n gap: 6px 10px;\n align-items: center;\n padding: 8px 25px;\n border-bottom: 1px rgba(0, 0, 0, 0.1) solid;\n\n .cell {\n display: flex;\n align-items: center;\n justify-content: center;\n }\n }\n .label {\n color: #35618e;\n font-size: 16px;\n letter-spacing: -0.05em;\n white-space: nowrap;\n display: flex;\n justify-content: center;\n }\n input {\n padding: 6px 8px;\n border: 1px solid rgba(0, 0, 0, 0.1);\n border-radius: 5px;\n background: #ffffff;\n color: #212529;\n font-size: 16px;\n margin-right: 5px;\n\n &:disabled {\n background: #f6f6f6;\n }\n }\n .unit {\n text-align: center;\n color: #212529;\n }\n .plus {\n color: #e13232;\n font-weight: 700;\n }\n .minus {\n color: #1e88e5;\n font-weight: 700;\n }\n .button-line {\n display: flex;\n justify-content: center;\n gap: 10px;\n margin-top: 16px;\n }\n .ghost-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 10px;\n background: #35618e;\n color: #ffffff;\n border-radius: 5px;\n cursor: pointer;\n }\n .ghost-btn.secondary {\n background: #24be7b;\n }\n .ghost-btn.disabled,\n .collect-btn.disabled {\n opacity: 0.45;\n cursor: not-allowed;\n }\n\n /* ── 자동 수집 패널 ── */\n .collect-panel {\n margin: 4px 6px 14px;\n border: 1px solid #d7e3f2;\n border-radius: 8px;\n background: linear-gradient(180deg, #f7fafe 0%, #ffffff 100%);\n overflow: hidden;\n }\n .collect-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 10px 16px;\n background: #eaf2fb;\n border-bottom: 1px solid #d7e3f2;\n }\n .collect-head .ttl {\n color: #0c4da2;\n font-weight: 700;\n font-size: 14px;\n letter-spacing: -0.03em;\n }\n .collect-head .ttl small {\n color: #5a7da6;\n font-weight: 400;\n margin-left: 8px;\n }\n .collect-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 7px 14px;\n background: #0c4da2;\n color: #fff;\n border-radius: 6px;\n cursor: pointer;\n font-size: 13px;\n font-weight: 600;\n }\n .collect-btn[disabled] {\n background: #9bb4d2;\n cursor: default;\n }\n .source-list {\n display: flex;\n gap: 10px;\n padding: 12px 16px;\n flex-wrap: wrap;\n }\n .source-card {\n flex: 1 1 200px;\n border: 1px solid #e3e9f0;\n border-radius: 7px;\n padding: 10px 12px;\n background: #fff;\n transition: border-color 0.2s, box-shadow 0.2s;\n }\n .source-card.collecting {\n border-color: #2e79be;\n box-shadow: 0 0 0 3px rgba(46, 121, 190, 0.12);\n }\n .source-card.done {\n border-color: #24be7b;\n }\n .source-card .sc-head {\n display: flex;\n align-items: center;\n gap: 8px;\n font-weight: 600;\n color: #212529;\n font-size: 13px;\n }\n .source-card .sc-head .sys-icon {\n font-size: 16px;\n }\n .source-card .sc-status {\n margin-left: auto;\n font-size: 12px;\n }\n .sc-status.idle {\n color: #aab4c0;\n }\n .sc-status.collecting {\n color: #2e79be;\n }\n .sc-status.done {\n color: #24be7b;\n font-weight: 700;\n }\n .sc-status.error {\n color: #8a8a8a;\n font-weight: 600;\n }\n .source-card.error {\n background: #fafafa;\n border-color: #e0e0e0;\n }\n .source-card .sc-error-msg {\n margin-top: 6px;\n padding: 6px 8px;\n border-radius: 4px;\n background: #f3f4f6;\n color: #6b7280;\n font-size: 12px;\n line-height: 1.4;\n word-break: break-word;\n }\n .source-card .sc-fields {\n margin-top: 8px;\n display: flex;\n flex-direction: column;\n gap: 3px;\n }\n .sc-field {\n font-size: 12px;\n color: #5a6b7b;\n display: flex;\n justify-content: space-between;\n }\n .sc-field b {\n color: #0c4da2;\n }\n .sc-desc {\n font-size: 11px;\n color: #9aa7b4;\n margin-top: 2px;\n }\n /* 가져온 값 출처 칩 */\n .src-chip {\n display: inline-flex;\n align-items: center;\n margin-left: 6px;\n padding: 1px 7px;\n font-size: 11px;\n border-radius: 10px;\n background: #e7f3ec;\n color: #1d9c63;\n white-space: nowrap;\n }\n .cell.just-filled input {\n animation: flash 1.1s ease;\n }\n /* 미입력 — kpiMetricValue 가 비어있는 metric 행 */\n .label.pending::before {\n content: '⚠';\n color: #e74c3c;\n margin-right: 4px;\n }\n .cell.pending input {\n border-color: #e74c3c;\n background-color: #fff5f5;\n }\n @keyframes flash {\n 0% {\n background: #fff7cc;\n }\n 100% {\n background: #ffffff;\n }\n }\n `\n ]\n\n @state() kpiMetricValues: any = []\n @state() kpiMetrics: any = []\n @property({ type: Object }) project: any = {}\n\n /** 연동 진행 상태: source → 'idle' | 'collecting' | 'done' */\n @state() collectStatus: Record<string, 'idle' | 'collecting' | 'done' | 'error'> = {}\n @state() collectError: Record<string, string> = {}\n /** 자동 수집한 실제값의 출처: metricId → 시스템 라벨 */\n @state() valueSources: Record<string, string> = {}\n /** 자동 수집한 계획값: projectKey → 값 (키스콘이 제공하는 계획공사기간/계획공사비) */\n @state() collectedPlan: Record<string, number> = {}\n /** 계획값 출처: projectKey → 시스템 라벨 */\n @state() planSources: Record<string, string> = {}\n /** 시스템별 수집 요약 (카드 표시용): 라벨 → [{label, text}] */\n @state() collectedSummary: Record<string, { label: string; text: string }[]> = {}\n /** 방금 채워진 셀 강조용 (metricId 또는 plan:projectKey) */\n @state() justFilled: Record<string, boolean> = {}\n @state() collecting = false\n\n /** kpi:input — Step1 계획값 입력/저장 권한 */\n @state() canSave = false\n /** kpi:auto-collect — 외부 시스템 자동 수집 권한 */\n @state() canAutoCollect = false\n\n /** 계획 칼럼을 갖는 항목 (키스콘이 계획값을 제공) */\n private static readonly PLAN_KEYS = ['constructionPeriod', 'constructionCost']\n\n render() {\n // 전월 (last month) YYYY-MM. 월별 metric 의 \"전월 데이터 입력\" 검사 기준.\n const lastMonth = moment().tz('Asia/Seoul').subtract(1, 'month').format('YYYY-MM')\n\n return html`\n <div class=\"title\">\n <div>\n 당초 계획 대비 실제 진행 과정에서 변동된 공사비, 공기(공사기간), 면적, 기타 주요 항목을 현실에 맞게 수정·입력합니다.\n <br />이 정보는 성과 분석, KPI 평가, 통계 산출 등에 기준값으로 사용되므로, 가능한 한 실제 값 기준으로 정확히\n 입력해주시기 바랍니다.\n </div>\n </div>\n\n ${this._renderCollectPanel()}\n\n <div class=\"rows\">\n <div class=\"row header\">\n <div class=\"header-label\">기본정보</div>\n <div class=\"header-label\">계획</div>\n <div class=\"header-label\">실제</div>\n <div class=\"header-label\">편차</div>\n </div>\n\n ${this.kpiMetrics.map(metric => {\n // 상수로 정의된 매핑 정보에서 metric.name으로 projectKey를 찾음\n const projectKey = KPI_METRIC_KEY_MAPPING.find(item => item.label === metric.name)?.projectKey || ''\n const isMonthly = metric.periodType === 'MONTH'\n // 현재값 lookup:\n // MONTH → 전월 row (단일 진실원)\n // ALLTIME → 단일 ALLTIME row\n // 그 외 → metricId+periodType 매칭\n const kpiMetricValue = isMonthly\n ? this.kpiMetricValues.find(\n (item: any) =>\n item.metricId === metric.id &&\n item.periodType === 'MONTH' &&\n (item.valueDate || '').startsWith(lastMonth)\n ) || {}\n : this.kpiMetricValues.find(\n (item: any) => item.metricId === metric.id && item.periodType === metric.periodType\n ) || {}\n const isDisplayInput = projectKey === 'constructionPeriod' || projectKey === 'constructionCost' // 유효한 계획 필드\n\n // planValue는 project에서 찾고 없으면 buildingComplex의 값에서 찾음\n const basePlanValue = this.project[projectKey] || this.project?.buildingComplex?.[projectKey] || 0\n let planValue = basePlanValue\n const unit = kpiMetricValue.unit || metric.unit || ''\n\n // 2. 계획 값 처리\n // 공사기간은 기간을 일 단위로 계산\n if (projectKey === 'constructionPeriod') {\n planValue = Math.ceil(calcDateDiff(this.project.startDate, this.project.endDate) / 30)\n } else if (!isDisplayInput) {\n planValue = 0\n }\n\n // 1. 실제 값 처리 kpi값을 찾고, 없으면 (NO_PLAN_FIELDS에 해당하는 필드는 원래 계획값 사용)\n const actualValue = kpiMetricValue.value ?? (NO_PLAN_FIELDS.includes(projectKey) ? basePlanValue : 0)\n\n const diffValue = calcDiff(planValue, actualValue)\n const diffClass = diffValue === 0 ? '' : diffValue > 0 ? 'plus' : 'minus'\n const diffSign = diffValue === 0 ? '' : diffValue > 0 ? '+' : '-'\n\n const src = this.valueSources[metric.id]\n // 계획값: 키스콘 수집값이 있으면 그것을, 없으면 계산된 planValue\n const planSrc = this.planSources[projectKey]\n const shownPlan = this.collectedPlan[projectKey] ?? planValue\n const planFilled = this.justFilled[`plan:${projectKey}`]\n // 계획값이 키스콘에서 들어왔으면 편차도 그 기준으로 재계산\n const effDiff = calcDiff(shownPlan, actualValue)\n const effDiffClass = effDiff === 0 ? '' : effDiff > 0 ? 'plus' : 'minus'\n const effDiffSign = effDiff === 0 ? '' : effDiff > 0 ? '+' : '-'\n\n // periodType 무관하게 값 없으면 pending. 메시지는 MONTH 만 \"전월 데이터 입력 필요\".\n const isPending = kpiMetricValue.value === undefined || kpiMetricValue.value === null\n\n return html`<div class=\"row\">\n <div class=\"label ${isPending ? 'pending' : ''}\"\n title=${isPending\n ? isMonthly\n ? '전월 데이터 입력 필요'\n : '아직 입력되지 않은 항목'\n : ''}>• ${metric.name}</div>\n <div class=\"cell ${planFilled ? 'just-filled' : ''}\">\n ${isDisplayInput\n ? html`<input\n numeric\n .value=${shownPlan ?? 0}\n @input=${(e: InputEvent) => this._onPlanInputChange(e, metric, projectKey)}\n />\n ${unit}\n ${planSrc ? html`<span class=\"src-chip\">🔗 ${planSrc}</span>` : ''}`\n : html`-`}\n </div>\n <div class=\"cell ${this.justFilled[metric.id] ? 'just-filled' : ''} ${isPending ? 'pending' : ''}\">\n <input numeric .value=${actualValue ?? 0} @input=${(e: InputEvent) => this._onInputChange(e, metric)} />\n ${unit}\n ${src ? html`<span class=\"src-chip\">🔗 ${src}</span>` : ''}\n </div>\n <div class=\"unit ${isDisplayInput ? effDiffClass : ''}\">\n ${isDisplayInput\n ? html`${effDiffSign} ${Math.abs(effDiff).toLocaleString()} ${unit}`\n : ''}\n </div>\n </div>`\n })}\n\n <div class=\"button-line\">\n <div class=\"ghost-btn\" @click=${this._reset}>초기화</div>\n <div\n class=\"ghost-btn secondary ${this.canSave ? '' : 'disabled'}\"\n title=${this.canSave ? '' : 'kpi:input 권한 필요'}\n @click=${() => this.canSave && this._save()}\n >\n 저장\n </div>\n </div>\n </div>\n `\n }\n\n private _renderCollectPanel() {\n return html`\n <div class=\"collect-panel\">\n <div class=\"collect-head\">\n <div class=\"ttl\">시스템 연동 자동 수집 <small>외부 시스템에서 기본정보를 가져옵니다</small></div>\n <div\n class=\"collect-btn ${this.canAutoCollect ? '' : 'disabled'}\"\n ?disabled=${this.collecting}\n title=${this.canAutoCollect ? '' : 'kpi:auto-collect 권한 필요'}\n @click=${() =>\n this.canAutoCollect && !this.collecting ? this._autoCollect() : null}\n >\n ${this.collecting ? '수집 중…' : '⟳ 자동 수집'}\n </div>\n </div>\n <div class=\"source-list\">\n ${INTEGRATION_SOURCES.map(meta => {\n const status = this.collectStatus[meta.source] || 'idle'\n const errMsg = this.collectError[meta.source]\n // 카드 안의 수집 필드 summary — 실제/계획 무관 collectedSummary 기준으로 통일.\n // (valueSources 만 보면 키스콘처럼 계획값만 채우는 시스템은 표시 안 됨)\n const collectedFields = this._collectedFieldsOf(meta.label)\n const statusText =\n status === 'collecting'\n ? '연동 중…'\n : status === 'done'\n ? '완료 ✓'\n : status === 'error'\n ? '정보 없음 ⓘ'\n : '대기'\n return html`\n <div class=\"source-card ${status}\">\n <div class=\"sc-head\">\n <span class=\"sys-icon\">${meta.icon}</span>\n <span>${meta.label}</span>\n <span class=\"sc-status ${status}\">${statusText}</span>\n </div>\n <div class=\"sc-desc\">${meta.desc}</div>\n ${status === 'error' && errMsg ? html`<div class=\"sc-error-msg\">${errMsg}</div>` : ''}\n ${status === 'done' && collectedFields.length\n ? html`<div class=\"sc-fields\">\n ${collectedFields.map(\n f => html`<div class=\"sc-field\"><span>${f.label}</span><b>${f.text}</b></div>`\n )}\n </div>`\n : ''}\n </div>\n `\n })}\n </div>\n </div>\n `\n }\n\n /** 패널 카드에 표시할, 해당 시스템에서 수집한 값 요약 */\n private _collectedFieldsOf(sourceLabel: string): { label: string; text: string }[] {\n return this.collectedSummary[sourceLabel] || []\n }\n\n /** 자동 수집 — 백엔드의 collectProjectExternalData mutation 1번 호출.\n * 서버가 시나리오 3종 (세움터/올바로/키스콘) 을 순차 실행하고 시스템별 ok/메시지를 반환.\n * 성공/실패와 무관하게 시나리오 step 이 KPI Value 를 DB 에 적재하므로 호출 후\n * KPI Metric Values 를 재조회해 form 을 자동 갱신.\n *\n * 운영 현실: 키스콘·올바로는 시공사 자격증명 미확보가 많아 오류 빈도 높음.\n * 세움터는 공공데이터 서비스키만으로 동작 가능. 시스템별로 카드에 독립 표시.\n */\n private async _autoCollect() {\n if (!this.project?.id) {\n notify({ message: '프로젝트 정보가 없습니다.' })\n return\n }\n this.collecting = true\n this.collectStatus = {}\n this.collectError = {}\n this.collectedSummary = {}\n this.collectedPlan = {}\n this.planSources = {}\n this.valueSources = {}\n\n // 모든 카드를 collecting 상태로 표시\n const collecting: Record<string, 'collecting'> = {}\n for (const meta of INTEGRATION_SOURCES) collecting[meta.source] = 'collecting'\n this.collectStatus = collecting\n\n try {\n const results = await collectProjectExternalData(this.project.id)\n\n // 시스템별 status / error 메시지 업데이트 + result data 를 form 에 매핑\n const status: Record<string, 'done' | 'error'> = {}\n const errors: Record<string, string> = {}\n for (const r of results) {\n status[r.source] = r.ok ? 'done' : 'error'\n if (!r.ok) {\n errors[r.source] = r.message\n continue\n }\n // 시나리오 result 객체 = { projectKey: value, ... }.\n // KPI_METRIC_KEY_MAPPING 매칭 키 → form 채움. 미매칭 키 (siteType,\n // structureType 등 프로젝트/BuildingComplex 기본 속성) → 카드 요약만.\n const summary: { label: string; text: string }[] = []\n // KPI_METRIC_KEY_MAPPING 에 없는 키들의 한글 라벨. BuildingComplex/Project 직접 속성용.\n const NON_KPI_LABEL: Record<string, string> = {\n siteType: '건축현장유형',\n structureType: '구조형태',\n coverageRatio: '건폐율',\n householdCount: '세대수',\n buildingCount: '동수',\n startDate: '착공일',\n endDate: '준공일',\n permitDate: '건축허가일',\n bldNm: '건물명'\n }\n for (const [projectKey, rawValue] of Object.entries(r.data || {})) {\n if (rawValue === null || rawValue === undefined || rawValue === '') continue\n // 비-primitive (객체/배열) 값은 시나리오 측 return 형태 오류 — form 매핑/표시\n // 모두 skip 하고 console 에 진단 로그만 남김. 흔한 원인은 시나리오 마지막 step 이\n // `return { result: { ... } }` 처럼 wrap 한 케이스.\n if (typeof rawValue === 'object') {\n console.warn(`[자동수집] '${r.label}'.${projectKey} 가 object — 시나리오 return 형태 점검`, rawValue)\n continue\n }\n const mapping = KPI_METRIC_KEY_MAPPING.find(x => x.projectKey === projectKey)\n const label = mapping?.label || NON_KPI_LABEL[projectKey] || projectKey\n const isPlan = SvProjectCompleteTab1Plan.PLAN_KEYS.includes(projectKey)\n\n // summary 카드 표시용 — string 값은 그대로, number 는 toLocaleString\n const numericValue = typeof rawValue === 'number' ? rawValue : Number(rawValue)\n const isNumeric = Number.isFinite(numericValue) && typeof rawValue !== 'string'\n const valueText = isNumeric ? numericValue.toLocaleString() : String(rawValue)\n summary.push({ label, text: valueText })\n\n // KPI 매핑이 없는 키 (siteType/structureType 등) 는 form 채움 skip — 단지 노출만.\n if (!mapping) continue\n\n if (isPlan) {\n // 계획값 (키스콘 제공) — 계획 칼럼 표시 + KpiMetric value 에도 upsert\n // 하여 _save 시 patches 에 포함. 사용자가 계획 input 직접 편집해도 같은 흐름.\n this.collectedPlan = { ...this.collectedPlan, [projectKey]: numericValue }\n this.planSources = { ...this.planSources, [projectKey]: r.label }\n this.justFilled = { ...this.justFilled, [`plan:${projectKey}`]: true }\n const planMetric = this.kpiMetrics.find(\n (m: any) => KPI_METRIC_KEY_MAPPING.find(x => x.label === m.name)?.projectKey === projectKey\n )\n if (planMetric) this._setMetricValue(planMetric, numericValue)\n } else {\n // 실제값 (세움터/올바로/기타) — 매칭되는 metric 의 실제 칼럼에 반영\n const metric = this.kpiMetrics.find(\n (m: any) => KPI_METRIC_KEY_MAPPING.find(x => x.label === m.name)?.projectKey === projectKey\n )\n if (!metric) continue\n this._setMetricValue(metric, numericValue)\n this.valueSources = { ...this.valueSources, [metric.id]: r.label }\n this.justFilled = { ...this.justFilled, [metric.id]: true }\n }\n }\n this.collectedSummary = { ...this.collectedSummary, [r.label]: summary }\n }\n this.collectStatus = status\n this.collectError = errors\n\n // 재조회 안 함 — 시나리오는 fetch + result 반환만 책임 (DB 적재 X). 직전에\n // _setMetricValue 가 memory 에 채운 값들을 DB 의 옛 값으로 덮어쓰면 자동수집\n // 결과가 폼에 반영되지 않음. 사용자가 [저장] 버튼 누르면 그제서야 DB 반영.\n this.requestUpdate()\n setTimeout(() => (this.justFilled = {}), 1300)\n\n const okCount = results.filter(r => r.ok).length\n if (okCount === results.length) {\n notify({ message: `외부 시스템 ${okCount}건 모두 수집 완료. 검토 후 [저장]을 눌러주세요.` })\n } else if (okCount === 0) {\n notify({ message: '외부 시스템에서 가져올 정보가 없습니다.' })\n } else {\n notify({ message: `외부 시스템 수집 — ${okCount}건 완료, 나머지는 정보 없음.` })\n }\n } catch (e) {\n // mutation 자체 실패 (네트워크/권한 등) — 모든 카드를 정보 없음 으로\n const msg = e instanceof Error ? e.message : String(e)\n console.warn('[자동수집] 전체 호출 실패', e)\n const status: Record<string, 'error'> = {}\n const errors: Record<string, string> = {}\n for (const meta of INTEGRATION_SOURCES) {\n status[meta.source] = 'error'\n errors[meta.source] = '업데이트할 정보가 없습니다'\n }\n this.collectStatus = status\n this.collectError = errors\n notify({ message: '외부 시스템 연동이 일시적으로 안 됩니다.' })\n } finally {\n this.collecting = false\n }\n }\n\n /**\n * 메트릭의 실제값 set/add — _onInputChange 와 동일하게 periodType 별 row upsert.\n */\n private _setMetricValue(metric: any, value: number | string) {\n this.kpiMetricValues = this._upsertValueForMetric(this.kpiMetricValues, metric, value)\n }\n\n async connectedCallback() {\n super.connectedCallback()\n const [canSave, canAutoCollect] = await Promise.all([\n hasPrivilege({ category: 'kpi', privilege: 'input', domainOwnerGranted: true, superUserGranted: true }),\n hasPrivilege({ category: 'kpi', privilege: 'auto-collect', domainOwnerGranted: true, superUserGranted: true })\n ])\n this.canSave = canSave\n this.canAutoCollect = canAutoCollect\n }\n\n willUpdate(changedProperties: Map<string, any>) {\n super.willUpdate(changedProperties)\n\n // project가 변경되고, project.id가 존재하면 데이터 로드\n if (changedProperties.has('project') && this.project?.id) {\n this._getInitData()\n }\n }\n\n private async _getInitData() {\n // getKpiMetrics 가 이미 active=true 만 반환. 여기선 평가 (Step2) 만 추가 제외.\n // 비활성화는 KPI 관리 admin 에서 active=false 처리.\n const kpiMetrics = await getKpiMetrics(this.project?.code)\n this.kpiMetrics = kpiMetrics.filter(item => !item.name.includes('평가')) || []\n this.kpiMetricValues = await getKpiMetricValues(this.project.id, this.project?.code)\n }\n\n /**\n * 공사비/공사기간 의 \"계획\" 컬럼 input 편집 핸들러 — collectedPlan 표시값 동기화 +\n * 해당 KpiMetric 의 value 도 upsert 하여 _save 시 patches 에 포함되도록.\n */\n private _onPlanInputChange(event: InputEvent, metric: any, projectKey: string) {\n const target = event.target as HTMLInputElement\n const inputVal = Number(target.value.replace(/[^\\d.]/g, ''))\n this.collectedPlan = { ...this.collectedPlan, [projectKey]: inputVal }\n this.kpiMetricValues = this._upsertValueForMetric(this.kpiMetricValues, metric, inputVal)\n }\n\n // Input 요소의 값이 변경될 때 호출되는 콜백 함수\n private _onInputChange(event: InputEvent, metric: any) {\n const target = event.target as HTMLInputElement\n let inputVal: any = target.value\n\n // 숫자 타입은 다른 문자 입력 제거\n if (target.hasAttribute('numeric')) {\n inputVal = Number(inputVal.replace(/[^\\d.]/g, ''))\n }\n\n this.kpiMetricValues = this._upsertValueForMetric(this.kpiMetricValues, metric, inputVal)\n }\n\n /**\n * metric.periodType 에 따라 적절한 row 를 upsert.\n * - MONTH : 전월 row (YYYY-MM-01). \"오늘 입력 = 지난달 데이터\" 운영 원칙.\n * - ALLTIME : 단일 row (valueDate sentinel 무관).\n * - 그 외 (DAY/WEEK/QUARTER/YEAR/RANGE) : metric.periodType 그대로, valueDate=today.\n * 매칭은 (metricId, periodType) 기준 — MONTH 만 추가로 valueDate prefix.\n */\n private _upsertValueForMetric(values: any[], metric: any, value: any): any[] {\n const today = moment().tz('Asia/Seoul')\n const todayYmd = today.format('YYYY-MM-DD')\n const periodType = metric.periodType\n let updated = [...values]\n\n if (periodType === 'MONTH') {\n const lastMonth = today.clone().subtract(1, 'month')\n const lastMonth1 = lastMonth.format('YYYY-MM-01')\n const lastMonthYm = lastMonth.format('YYYY-MM')\n const idx = updated.findIndex(\n (i: any) =>\n i.metricId === metric.id &&\n i.periodType === 'MONTH' &&\n (i.valueDate || '').startsWith(lastMonthYm)\n )\n if (idx !== -1) {\n updated[idx] = { ...updated[idx], value }\n } else {\n updated.push({\n id: crypto.randomUUID(),\n value,\n metricId: metric.id,\n unit: metric.unit || '',\n org: this.project.id,\n periodType: 'MONTH',\n valueDate: lastMonth1\n })\n }\n } else {\n // ALLTIME / DAY / WEEK / QUARTER / YEAR / RANGE — metric.periodType 그대로 단일 row upsert.\n const idx = updated.findIndex(\n (i: any) => i.metricId === metric.id && i.periodType === periodType\n )\n if (idx !== -1) {\n updated[idx] = { ...updated[idx], value }\n } else {\n updated.push({\n id: crypto.randomUUID(),\n value,\n metricId: metric.id,\n unit: metric.unit || '',\n org: this.project.id,\n periodType,\n valueDate: todayYmd\n })\n }\n }\n\n return updated\n }\n\n private async _save() {\n // 사용자가 실제 편집한 row 만 저장. 자동 산정 metric (계획/실제 공사비·공기) 는\n // 별도의 데이터 출처(project 정보, 키스콘 수집, 다른 metric 의 전월 row 등)에서\n // 도출돼야 하므로 이 화면에서 하드코딩 derive 하지 않음.\n const response = await updateProjectCompleteStep1(this.kpiMetricValues, this.project?.code)\n if (!response.errors) {\n notify({ message: '저장되었습니다.' })\n }\n }\n\n private _reset() {\n this._getInitData()\n }\n}\n"]}
@@ -61,7 +61,7 @@ let SvProjectCompletedListPage = class SvProjectCompletedListPage extends Scoped
61
61
  var _a, _b, _c, _d, _e, _f, _g, _h, _j;
62
62
  const areaText = `연면적 : ${((_b = (_a = project === null || project === void 0 ? void 0 : project.buildingComplex) === null || _a === void 0 ? void 0 : _a.area) === null || _b === void 0 ? void 0 : _b.toLocaleString()) || '-'} ㎡`;
63
63
  const costText = `공사비 : ${((_d = (_c = project === null || project === void 0 ? void 0 : project.buildingComplex) === null || _c === void 0 ? void 0 : _c.constructionCost) === null || _d === void 0 ? void 0 : _d.toLocaleString()) || '-'} 억원`;
64
- const householdCountText = `세대수 : ${((_f = (_e = project === null || project === void 0 ? void 0 : project.buildingComplex) === null || _e === void 0 ? void 0 : _e.householdCount) === null || _f === void 0 ? void 0 : _f.toLocaleString()) || '-'} 세대`;
64
+ const workerCountText = `투입인력 : ${((_f = (_e = project === null || project === void 0 ? void 0 : project.buildingComplex) === null || _e === void 0 ? void 0 : _e.workerCount) === null || _f === void 0 ? void 0 : _f.toLocaleString()) || '-'} M/M`;
65
65
  const kpiValue = project.kpi ? project.kpi : 0;
66
66
  return html `
67
67
  <div tr @click=${() => navigate(`project-detail/${project.id}`)}>
@@ -74,7 +74,7 @@ let SvProjectCompletedListPage = class SvProjectCompletedListPage extends Scoped
74
74
  <div class="sub">주소 : ${((_h = project.buildingComplex) === null || _h === void 0 ? void 0 : _h.address) || ''}</div>
75
75
  <div class="sub">착공~준공 : ${project.startDate} ~ ${project.endDate}</div>
76
76
  </div>
77
- <div td-etc>${areaText + '\n' + costText + '\n' + householdCountText}</div>
77
+ <div td-etc>${areaText + '\n' + costText + '\n' + workerCountText}</div>
78
78
  <div td-request>KPI입력 요청 - 건</div>
79
79
  <div td-kpi>
80
80
  <kpi-single-boxplot-chart .data=${this.getBoxPlotDataForProject(project)}></kpi-single-boxplot-chart>
@@ -126,7 +126,7 @@ let SvProjectCompletedListPage = class SvProjectCompletedListPage extends Scoped
126
126
  area
127
127
  clientCompany
128
128
  constructionCost
129
- householdCount
129
+ workerCount
130
130
  }
131
131
  }
132
132
  total