@dssp/dkpi 1.0.0-alpha.96 → 1.0.0-alpha.97
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js +10 -1
- package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js.map +1 -1
- package/dist-client/pages/sv-project-update.js +10 -0
- package/dist-client/pages/sv-project-update.js.map +1 -1
- package/dist-client/shared/complete-api.d.ts +6 -0
- package/dist-client/shared/complete-api.js +18 -0
- package/dist-client/shared/complete-api.js.map +1 -1
- package/dist-client/shared/kpi-project-sync.d.ts +15 -0
- package/dist-client/shared/kpi-project-sync.js +127 -0
- package/dist-client/shared/kpi-project-sync.js.map +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
|
@@ -5,6 +5,7 @@ import { customElement, property, state } from 'lit/decorators.js';
|
|
|
5
5
|
import { calcDateDiff } from '../../shared/func';
|
|
6
6
|
import { getKpiMetricValues, getKpiMetrics, updateProjectCompleteStep1, collectProjectExternalData } from '../../shared/complete-api';
|
|
7
7
|
import { INTEGRATION_SOURCES } from '../../shared/integration-fetch';
|
|
8
|
+
import { syncKpiToProjectInfo } from '../../shared/kpi-project-sync';
|
|
8
9
|
import moment from 'moment-timezone';
|
|
9
10
|
import { notify } from '@operato/layout';
|
|
10
11
|
import { hasPrivilege } from '@things-factory/auth-base/dist-client';
|
|
@@ -425,12 +426,20 @@ let SvProjectCompleteTab1Plan = SvProjectCompleteTab1Plan_1 = class SvProjectCom
|
|
|
425
426
|
return updated;
|
|
426
427
|
}
|
|
427
428
|
async _save() {
|
|
428
|
-
var _a;
|
|
429
|
+
var _a, _b;
|
|
429
430
|
// 사용자가 실제 편집한 row 만 저장. 자동 산정 metric (계획/실제 공사비·공기) 는
|
|
430
431
|
// 별도의 데이터 출처(project 정보, 키스콘 수집, 다른 metric 의 전월 row 등)에서
|
|
431
432
|
// 도출돼야 하므로 이 화면에서 하드코딩 derive 하지 않음.
|
|
432
433
|
const response = await updateProjectCompleteStep1(this.kpiMetricValues, (_a = this.project) === null || _a === void 0 ? void 0 : _a.code);
|
|
433
434
|
if (!response.errors) {
|
|
435
|
+
// 양방향 동기 — 연면적/용적율/투입인력/설계변경건 값을 BuildingComplex(프로젝트 정보)에도 반영.
|
|
436
|
+
// 보조 동기 실패가 저장 성공 UX 를 막지 않도록 warn 만.
|
|
437
|
+
try {
|
|
438
|
+
await syncKpiToProjectInfo(this.project, this.kpiMetrics, this.kpiMetricValues, (_b = this.project) === null || _b === void 0 ? void 0 : _b.code);
|
|
439
|
+
}
|
|
440
|
+
catch (e) {
|
|
441
|
+
console.warn('[KPI→프로젝트정보] BuildingComplex 동기 실패', e);
|
|
442
|
+
}
|
|
434
443
|
notify({ message: '저장되었습니다.' });
|
|
435
444
|
}
|
|
436
445
|
}
|
|
@@ -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 { 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: '총 투입인력(M/M)', 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 320px 1fr;\n padding: 0px 25px;\n\n .header-label {\n color: #212529;\n text-align: center;\n }\n }\n /* '기본정보' / '월별 데이터' 섹션 사이 간격 */\n .row.header.section-gap {\n margin-top: 24px;\n }\n .row {\n display: grid;\n grid-template-columns: 220px 320px 1fr;\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: flex-start;\n flex-wrap: nowrap;\n white-space: nowrap;\n gap: 6px;\n padding-right: 12px;\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: flex-end;\n padding-right: 12px;\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 text-align: right;\n\n &:disabled {\n background: #f6f6f6;\n }\n }\n .unit {\n text-align: center;\n color: #212529;\n }\n .desc {\n color: #5a6168;\n font-size: 12px;\n line-height: 1.4;\n padding-left: 8px;\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:\n border-color 0.2s,\n 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 ${this._renderSection(\n '기본정보',\n this.kpiMetrics.filter((m: any) => m.periodType !== 'MONTH'),\n lastMonth\n )}\n ${\n // '월별 데이터' 섹션 당분간 숨김. 복원하려면 아래 주석을 해제하고 이 '' 를 제거.\n // this._renderSection('월별 데이터', this.kpiMetrics.filter((m: any) => m.periodType === 'MONTH'), lastMonth, true)\n ''\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 /** '기본정보' / '월별 데이터' 섹션 렌더. 해당 그룹에 metric 이 없으면 섹션 자체를 생략. */\n private _renderSection(title: string, metrics: any[], lastMonth: string, topGap = false) {\n if (!metrics.length) return ''\n return html`\n <div class=\"row header ${topGap ? 'section-gap' : ''}\">\n <div class=\"header-label\">${title}</div>\n <div class=\"header-label\">값</div>\n <div class=\"header-label\">설명</div>\n </div>\n ${metrics.map(metric => this._renderMetricRow(metric, lastMonth))}\n `\n }\n\n private _renderMetricRow(metric: any, lastMonth: string) {\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 && item.periodType === 'MONTH' && (item.valueDate || '').startsWith(lastMonth)\n ) || {}\n : this.kpiMetricValues.find((item: any) => item.metricId === metric.id && item.periodType === metric.periodType) || {}\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 src = this.valueSources[metric.id]\n\n // periodType 무관하게 값 없으면 pending. 메시지는 MONTH 만 \"전월 데이터 입력 필요\".\n const isPending = kpiMetricValue.value === undefined || kpiMetricValue.value === null\n\n return html`<div class=\"row\">\n <div\n class=\"label ${isPending ? 'pending' : ''}\"\n title=${isPending ? (isMonthly ? '전월 데이터 입력 필요' : '아직 입력되지 않은 항목') : ''}\n >\n ${metric.name}\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} ${src ? html`<span class=\"src-chip\">🔗 ${src}</span>` : ''}\n </div>\n <div class=\"desc\">${metric.description || ''}</div>\n </div>`\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=${() => (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' ? '연동 중…' : status === 'done' ? '완료 ✓' : status === 'error' ? '정보 없음 ⓘ' : '대기'\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(f => html`<div class=\"sc-field\"><span>${f.label}</span><b>${f.text}</b></div>`)}\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 // 평가(Step2) 만 제외. 나머지는 기본정보 탭에서 '기본정보'/'월별 데이터' 두 섹션으로 구분 렌더.\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) => i.metricId === metric.id && i.periodType === 'MONTH' && (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((i: any) => i.metricId === metric.id && i.periodType === periodType)\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 { 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 { syncKpiToProjectInfo } from '../../shared/kpi-project-sync'\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: '총 투입인력(M/M)', 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 320px 1fr;\n padding: 0px 25px;\n\n .header-label {\n color: #212529;\n text-align: center;\n }\n }\n /* '기본정보' / '월별 데이터' 섹션 사이 간격 */\n .row.header.section-gap {\n margin-top: 24px;\n }\n .row {\n display: grid;\n grid-template-columns: 220px 320px 1fr;\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: flex-start;\n flex-wrap: nowrap;\n white-space: nowrap;\n gap: 6px;\n padding-right: 12px;\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: flex-end;\n padding-right: 12px;\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 text-align: right;\n\n &:disabled {\n background: #f6f6f6;\n }\n }\n .unit {\n text-align: center;\n color: #212529;\n }\n .desc {\n color: #5a6168;\n font-size: 12px;\n line-height: 1.4;\n padding-left: 8px;\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:\n border-color 0.2s,\n 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 ${this._renderSection(\n '기본정보',\n this.kpiMetrics.filter((m: any) => m.periodType !== 'MONTH'),\n lastMonth\n )}\n ${\n // '월별 데이터' 섹션 당분간 숨김. 복원하려면 아래 주석을 해제하고 이 '' 를 제거.\n // this._renderSection('월별 데이터', this.kpiMetrics.filter((m: any) => m.periodType === 'MONTH'), lastMonth, true)\n ''\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 /** '기본정보' / '월별 데이터' 섹션 렌더. 해당 그룹에 metric 이 없으면 섹션 자체를 생략. */\n private _renderSection(title: string, metrics: any[], lastMonth: string, topGap = false) {\n if (!metrics.length) return ''\n return html`\n <div class=\"row header ${topGap ? 'section-gap' : ''}\">\n <div class=\"header-label\">${title}</div>\n <div class=\"header-label\">값</div>\n <div class=\"header-label\">설명</div>\n </div>\n ${metrics.map(metric => this._renderMetricRow(metric, lastMonth))}\n `\n }\n\n private _renderMetricRow(metric: any, lastMonth: string) {\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 && item.periodType === 'MONTH' && (item.valueDate || '').startsWith(lastMonth)\n ) || {}\n : this.kpiMetricValues.find((item: any) => item.metricId === metric.id && item.periodType === metric.periodType) || {}\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 src = this.valueSources[metric.id]\n\n // periodType 무관하게 값 없으면 pending. 메시지는 MONTH 만 \"전월 데이터 입력 필요\".\n const isPending = kpiMetricValue.value === undefined || kpiMetricValue.value === null\n\n return html`<div class=\"row\">\n <div\n class=\"label ${isPending ? 'pending' : ''}\"\n title=${isPending ? (isMonthly ? '전월 데이터 입력 필요' : '아직 입력되지 않은 항목') : ''}\n >\n ${metric.name}\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} ${src ? html`<span class=\"src-chip\">🔗 ${src}</span>` : ''}\n </div>\n <div class=\"desc\">${metric.description || ''}</div>\n </div>`\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=${() => (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' ? '연동 중…' : status === 'done' ? '완료 ✓' : status === 'error' ? '정보 없음 ⓘ' : '대기'\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(f => html`<div class=\"sc-field\"><span>${f.label}</span><b>${f.text}</b></div>`)}\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 // 평가(Step2) 만 제외. 나머지는 기본정보 탭에서 '기본정보'/'월별 데이터' 두 섹션으로 구분 렌더.\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) => i.metricId === metric.id && i.periodType === 'MONTH' && (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((i: any) => i.metricId === metric.id && i.periodType === periodType)\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 // 양방향 동기 — 연면적/용적율/투입인력/설계변경건 값을 BuildingComplex(프로젝트 정보)에도 반영.\n // 보조 동기 실패가 저장 성공 UX 를 막지 않도록 warn 만.\n try {\n await syncKpiToProjectInfo(this.project, this.kpiMetrics, this.kpiMetricValues, this.project?.code)\n } catch (e) {\n console.warn('[KPI→프로젝트정보] BuildingComplex 동기 실패', e)\n }\n notify({ message: '저장되었습니다.' })\n }\n }\n\n private _reset() {\n this._getInitData()\n }\n}\n"]}
|
|
@@ -15,6 +15,7 @@ import '@dssp/project/dist-client/pages/lib/select2-component';
|
|
|
15
15
|
import './component/project-update-header';
|
|
16
16
|
import { isProjectTypeDomain, tenantHeaders } from '@dssp/project/dist-client/shared/domain-context';
|
|
17
17
|
import { inferGeoGroupFromPostalCode } from '../shared/geo-group-mapping';
|
|
18
|
+
import { syncProjectInfoToKpi } from '../shared/kpi-project-sync';
|
|
18
19
|
let SvProjectUpdate = class SvProjectUpdate extends ScopedElementsMixin(PageView) {
|
|
19
20
|
constructor() {
|
|
20
21
|
super(...arguments);
|
|
@@ -562,6 +563,15 @@ let SvProjectUpdate = class SvProjectUpdate extends ScopedElementsMixin(PageView
|
|
|
562
563
|
}
|
|
563
564
|
});
|
|
564
565
|
if (!response.errors) {
|
|
566
|
+
// 양방향 동기 — 연면적/용적율/투입인력/설계변경건을 KPI 기준정보(KpiMetricValue)에 반영하고,
|
|
567
|
+
// 공사일정(startDate/endDate)은 KPI "계획기간"(constructionPeriod)으로 단방향 반영.
|
|
568
|
+
// 보조 동기 실패가 저장 성공 UX 를 막지 않도록 warn 만.
|
|
569
|
+
try {
|
|
570
|
+
await syncProjectInfoToKpi(this.project, projectCode);
|
|
571
|
+
}
|
|
572
|
+
catch (e) {
|
|
573
|
+
console.warn('[프로젝트정보→KPI] KpiMetricValue 동기 실패', e);
|
|
574
|
+
}
|
|
565
575
|
notify({ message: '저장에 성공하였습니다.' });
|
|
566
576
|
}
|
|
567
577
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sv-project-update.js","sourceRoot":"","sources":["../../client/pages/sv-project-update.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@material/web/button/elevated-button.js'\nimport '@material/web/textfield/outlined-text-field.js'\n\nimport { PageView } from '@operato/shell'\nimport { PageLifecycle } from '@operato/shell/dist/src/app/pages/page-view'\nimport { css, html } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\nimport { ScopedElementsMixin } from '@open-wc/scoped-elements'\nimport { client } from '@operato/graphql'\nimport { notify } from '@operato/layout'\nimport { OxPrompt } from '@operato/popup'\nimport gql from 'graphql-tag'\nimport { Project, SiteType, SITE_TYPE_DISPLAY } from '@dssp/project/dist-client/pages/project/project-list'\nimport '@dssp/project/dist-client/pages/lib/select2-component'\nimport './component/project-update-header'\nimport { isProjectTypeDomain, tenantHeaders } from '@dssp/project/dist-client/shared/domain-context'\nimport { inferGeoGroupFromPostalCode } from '../shared/geo-group-mapping'\n\n@customElement('sv-project-update')\nexport class SvProjectUpdate extends ScopedElementsMixin(PageView) {\n static styles = [\n css`\n :host {\n display: grid;\n grid-template-rows: 55px auto;\n color: #4e5055;\n\n width: 100%;\n background-color: var(--md-sys-color-background, #f6f6f6);\n overflow-y: auto;\n\n --grid-record-emphasized-background-color: red;\n --grid-record-emphasized-color: yellow;\n }\n\n md-outlined-text-field {\n width: 100%;\n\n --md-outlined-text-field-container-shape: 5px;\n --md-outlined-text-field-outline-color: rgba(51, 51, 51, 0.2);\n --md-outlined-text-field-focus-outline-color: #1f7fd9;\n --md-outlined-text-field-focus-outline-width: 1px;\n --md-sys-color-primary: #586878;\n --md-outlined-text-field-input-text-size: 14px;\n --md-outlined-field-bottom-space: 3px;\n --md-outlined-field-top-space: 3px;\n --md-outlined-field-leading-space: var(--spacing-medium, 8px);\n --md-outlined-field-trailing-space: var(--spacing-medium, 8px);\n }\n select2-component {\n width: 100%;\n }\n md-filled-select {\n width: 100%;\n --md-filled-select-text-field-container-color: transparent;\n --md-filled-select-text-field-active-indicator-color: #999;\n --md-filled-select-text-field-input-text-size: 14px;\n --md-filled-select-text-field-input-text-line-height: 6px;\n }\n input[type='date'] {\n border: 1px solid rgba(51, 51, 51, 0.2);\n padding: var(--spacing-small, 4px) var(--spacing-medium, 8px);\n border-radius: 5px;\n }\n md-outlined-text-field[type='textarea'] {\n width: 100%;\n height: 120px;\n resize: none;\n }\n\n ox-input-image {\n width: 120px;\n height: 90px;\n }\n ox-input-file {\n width: 120px;\n height: 90px;\n padding: 0;\n }\n\n div[body] {\n display: flex;\n margin: var(--spacing-large, 12px);\n margin-top: 0;\n gap: var(--spacing-medium, 8px);\n\n & > div {\n display: flex;\n height: fit-content;\n flex-direction: column;\n padding: var(--spacing-large, 12px);\n background-color: var(--md-sys-color-on-primary);\n border: 1px solid #cccccc80;\n border-radius: 5px;\n gap: var(--spacing-medium, 8px);\n\n h3 {\n color: #2e79be;\n font-size: 16px;\n margin: 0px;\n }\n\n div[row] {\n display: flex;\n align-items: center;\n\n span:first-child {\n flex: 0.3;\n text-align: right;\n font-size: 14px;\n }\n\n span:last-child {\n flex: 0.7;\n display: flex;\n gap: var(--spacing-medium, 8px);\n margin-left: var(--spacing-medium, 8px);\n align-items: center;\n\n &.address-row {\n md-outlined-text-field {\n flex: 1;\n }\n .info-link-btn {\n flex: 0 0 auto;\n --md-elevated-button-container-color: #2e79be;\n --md-elevated-button-label-text-color: #fff;\n --md-elevated-button-container-height: 36px;\n }\n }\n\n &[align-end] {\n align-items: end;\n }\n }\n }\n }\n\n div[project-info] {\n flex: 0.55;\n }\n\n div[document-preview] {\n width: 100%;\n font-size: 12px;\n color: #2e79be;\n font-weight: 500;\n background-color: #f8f9fa;\n padding: 8px 12px;\n border-radius: 4px;\n border-left: 3px solid #2e79be;\n margin-top: 4px;\n }\n\n div[detail-info] {\n flex: 0.45;\n padding: 0px;\n gap: 10px;\n background-color: transparent;\n border: none;\n\n & > div {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-medium, 8px);\n border: 1px solid #cccccc80;\n border-radius: 5px;\n padding: var(--spacing-large, 12px);\n background-color: var(--md-sys-color-on-primary);\n\n md-outlined-text-field[type='text'] {\n width: 60%;\n }\n\n md-elevated-button {\n --md-elevated-button-container-height: 30px;\n --md-elevated-button-container-color: var(--md-sys-color-on-primary);\n --md-elevated-button-label-text-size: 16px;\n }\n hr {\n border: 1px rgba(51, 51, 51, 0.1) dashed;\n width: 100%;\n margin-bottom: var(--spacing-tiny, 2px);\n }\n div[warn] {\n font-size: 12px;\n color: red;\n margin-left: var(--spacing-small, 4px);\n margin-bottom: var(--spacing-small, 4px);\n }\n\n md-outlined-button {\n min-height: 30px;\n padding: 0 var(--spacing-large, 12px);\n box-shadow: 1px 1px 1px #0000001a;\n --md-outlined-button-label-text-color: #586878;\n --md-outlined-button-label-text-weight: bold;\n --md-sys-color-outline: rgba(51, 51, 51, 0.2);\n }\n\n div[row] {\n span:first-child {\n flex: 0.2;\n }\n span:last-child {\n flex: 0.8;\n }\n }\n\n div[separate-container] {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n gap: 8px 30px;\n margin-left: 15px;\n\n md-outlined-text-field {\n width: 80px;\n }\n\n & > div {\n display: flex;\n gap: 15px;\n\n span[floor-row] {\n display: inline-flex;\n align-items: center;\n margin-left: 5px;\n }\n\n span[floor-title] {\n min-width: 33px;\n margin-right: var(--spacing-small, 4px);\n }\n span:first-child {\n flex: 0.4;\n justify-content: flex-start;\n display: flex;\n }\n span:last-child {\n flex: 0.6;\n display: flex;\n align-items: center;\n padding-left: var(--spacing-medium, 8px);\n }\n }\n\n div[basement-container] {\n display: flex;\n align-items: center;\n gap: 4px;\n\n div[basement-checkbox] {\n display: flex;\n align-items: center;\n cursor: pointer;\n\n md-icon {\n --md-icon-size: 25px;\n color: #808080;\n transition: opacity 0.2s ease;\n }\n\n &[disabled] {\n cursor: not-allowed;\n opacity: 0.4;\n\n md-icon {\n color: #cccccc;\n }\n }\n\n &[checked] md-icon {\n background-color: #2e79be;\n color: white;\n border-radius: 3px;\n }\n }\n\n md-outlined-text-field[basement-floor] {\n width: 70px;\n margin-left: var(--spacing-small, 4px);\n opacity: 1;\n transition: opacity 0.2s ease;\n\n &[disabled] {\n opacity: 0.4;\n pointer-events: none;\n }\n }\n }\n }\n\n &[project] {\n div[separate-container] {\n margin-left: 15px;\n\n & > div {\n align-items: center;\n\n span:first-child {\n flex: 0.4;\n min-width: 100px;\n }\n span:last-child {\n flex: 0.4;\n margin-left: 0;\n }\n }\n }\n }\n }\n }\n }\n `\n ]\n\n get context() {\n return {\n title:\n !isProjectTypeDomain() && this.project?.name\n ? `프로젝트 정보 관리 - ${this.project?.name}`\n : '프로젝트 정보 관리'\n }\n }\n\n private defaultProject = {\n name: '',\n documentNaming: '',\n buildingComplex: {\n address: '',\n area: 0,\n constructionCompany: '',\n clientCompany: '',\n supervisoryCompany: '',\n designCompany: '',\n constructionType: '',\n buildings: []\n }\n }\n @state() projectId: string = ''\n @state() project: Project = { ...this.defaultProject }\n @state() selectedValues: string[] = []\n @state() overallConstructorList: Array<{ name: string; value: string }> = []\n @state() taskConstructorList: Array<{ name: string; value: string }> = []\n @state() taskSupervisoryList: Array<{ name: string; value: string }> = []\n @state() overallSupervisoryList: Array<{ name: string; value: string }> = []\n @state() collectingBasicInfo: boolean = false\n\n render() {\n return html`\n <dkpi-project-update-header\n .projectId=${this.project.id || ''}\n .projectState=${(this.project as any).state || ''}\n title=\"프로젝트 정보 관리\"\n @custom-click=${this._saveProject}\n @unfinalize=${this._onUnfinalize}\n ></dkpi-project-update-header>\n\n <div body>\n <div project-info>\n <h3>기본 정보</h3>\n <div row>\n <span>프로젝트명</span>\n <span\n ><md-outlined-text-field\n type=\"text\"\n name=\"name\"\n project\n .value=${this.project.name || ''}\n @input=${this._onInputChange}\n ></md-outlined-text-field>\n </span>\n </div>\n <div row>\n <span>프로젝트 주소</span>\n <span class=\"address-row\">\n <md-outlined-text-field\n type=\"text\"\n name=\"address\"\n building-complex\n .value=${this.project?.buildingComplex?.address || ''}\n @input=${this._onInputChange}\n ></md-outlined-text-field>\n <md-elevated-button\n class=\"info-link-btn\"\n ?disabled=${this.collectingBasicInfo}\n @click=${() => this._collectBasicInfo()}\n title=\"입력된 주소로 세움터 건축물대장 정보를 가져와 폼을 채웁니다\"\n >\n ${this.collectingBasicInfo ? '연동 중…' : '🔗 정보 연동'}\n </md-elevated-button>\n </span>\n </div>\n <div row>\n <span>우편번호</span>\n <span>\n <md-outlined-text-field\n type=\"text\"\n name=\"postalCode\"\n project\n .value=${(this.project as any).postalCode || ''}\n @input=${this._onPostalCodeChange}\n ></md-outlined-text-field>\n </span>\n </div>\n <div row>\n <span>연 면적</span>\n <span align-end\n ><md-outlined-text-field\n type=\"text\"\n name=\"area\"\n numeric\n building-complex\n .value=${this.project?.buildingComplex?.area?.toString() || ''}\n @input=${this._onInputChange}\n suffix-text=\"㎡\"\n ></md-outlined-text-field>\n </span>\n </div>\n\n <div row>\n <span>용적률</span>\n <span align-end>\n <md-outlined-text-field\n type=\"text\"\n name=\"floorAreaRatio\"\n numeric\n building-complex\n .value=${this.project?.buildingComplex?.floorAreaRatio?.toString() || ''}\n @input=${this._onInputChange}\n suffix-text=\"%\"\n ></md-outlined-text-field>\n </span>\n </div>\n\n <div row>\n <span>착공일정 ~ 준공일정</span>\n <span\n ><input\n type=\"date\"\n name=\"startDate\"\n project\n .value=${this.project.startDate || ''}\n @input=${this._onInputChange}\n max=\"9999-12-31\"\n />\n ~\n <input\n type=\"date\"\n name=\"endDate\"\n project\n .value=${this.project.endDate || ''}\n @input=${this._onInputChange}\n max=\"9999-12-31\"\n />\n </span>\n </div>\n\n <div row>\n <span>유형</span>\n <span\n ><md-filled-select name=\"sectorType\" project @change=${this._onInputChange}>\n <md-select-option ?selected=${!(this.project as any).sectorType} value=\"\">\n <div slot=\"headline\">선택</div>\n </md-select-option>\n <md-select-option ?selected=${(this.project as any).sectorType === 'PUBLIC'} value=\"PUBLIC\">\n <div slot=\"headline\">공공</div>\n </md-select-option>\n <md-select-option ?selected=${(this.project as any).sectorType === 'PRIVATE'} value=\"PRIVATE\">\n <div slot=\"headline\">민간</div>\n </md-select-option>\n </md-filled-select>\n </span>\n </div>\n <div row>\n <span>용도</span>\n <span\n ><md-filled-select name=\"buildingUsage\" project @change=${this._onInputChange}>\n <md-select-option ?selected=${!(this.project as any).buildingUsage} value=\"\">\n <div slot=\"headline\">선택</div>\n </md-select-option>\n <md-select-option\n ?selected=${(this.project as any).buildingUsage === 'RESIDENTIAL'}\n value=\"RESIDENTIAL\"\n >\n <div slot=\"headline\">주거</div>\n </md-select-option>\n <md-select-option\n ?selected=${(this.project as any).buildingUsage === 'NON_RESIDENTIAL'}\n value=\"NON_RESIDENTIAL\"\n >\n <div slot=\"headline\">비주거</div>\n </md-select-option>\n </md-filled-select>\n </span>\n </div>\n\n <div row>\n <span>발주처</span>\n <span\n ><md-outlined-text-field\n type=\"text\"\n name=\"clientCompany\"\n building-complex\n .value=${this.project?.buildingComplex?.clientCompany || ''}\n @input=${this._onInputChange}\n ></md-outlined-text-field>\n </span>\n </div>\n <div row>\n <span>건설사</span>\n <span\n ><md-outlined-text-field\n type=\"text\"\n name=\"constructionCompany\"\n building-complex\n .value=${this.project?.buildingComplex?.constructionCompany || ''}\n @input=${this._onInputChange}\n ></md-outlined-text-field>\n </span>\n </div>\n <div row>\n <span>설계사</span>\n <span\n ><md-outlined-text-field\n type=\"text\"\n name=\"designCompany\"\n building-complex\n .value=${this.project?.buildingComplex?.designCompany || ''}\n @input=${this._onInputChange}\n ></md-outlined-text-field>\n </span>\n </div>\n <div row>\n <span>감리사</span>\n <span\n ><md-outlined-text-field\n type=\"text\"\n name=\"supervisoryCompany\"\n building-complex\n .value=${this.project?.buildingComplex?.supervisoryCompany || ''}\n @input=${this._onInputChange}\n ></md-outlined-text-field>\n </span>\n </div>\n\n <div row>\n <span>대표사진 업로드</span>\n <span>\n <ox-input-image\n name=\"mainPhoto\"\n value=${this.project?.mainPhoto?.fullpath || ''}\n @change=${this.onCreateAttachment.bind(this)}\n ></ox-input-image>\n </span>\n </div>\n <div row>\n <span>공사금액</span>\n <span\n ><md-outlined-text-field\n type=\"text\"\n name=\"constructionCost\"\n numeric\n building-complex\n .value=${this.project?.buildingComplex?.constructionCost?.toString() || ''}\n @input=${this._onInputChange}\n suffix-text=\"억원\"\n ></md-outlined-text-field>\n </span>\n </div>\n <div row>\n <span>투입 인력</span>\n <span\n ><md-outlined-text-field\n type=\"text\"\n name=\"workerCount\"\n numeric\n building-complex\n .value=${this.project?.buildingComplex?.workerCount?.toString() || ''}\n @input=${this._onInputChange}\n suffix-text=\"명\"\n ></md-outlined-text-field>\n </span>\n </div>\n </div>\n\n <div detail-info>\n <div>\n <h3>테넌트 관리</h3>\n <div row>\n <span>관리번호</span>\n <span>\n ${this.project.tenantDomain\n ? html`<a href=${`/project/${this.project.code}/`} target=\"_blank\" title=\"테넌트로 이동\"\n >${this.project.code} <md-icon style=\"font-size:14px;vertical-align:middle\">open_in_new</md-icon></a\n >`\n : this.project.code\n ? html`${this.project.code}`\n : html`<span style=\"color:#999\">미발번 (승격 시 자동 부여)</span>`}\n </span>\n </div>\n <div row>\n <span>현재 상태</span>\n <span>\n ${this.project.tenantDomain\n ? html`<b style=\"color:#42b382\">활성 테넌트</b>`\n : this.project.code\n ? html`<b style=\"color:#aa6633\">강등됨</b> (재승격 시 ${this.project.code} 재사용)`\n : html`<b style=\"color:#999\">미승격</b>`}\n </span>\n </div>\n <div row>\n <span></span>\n <span tenant-actions>\n ${this.project.tenantDomain\n ? html`<md-elevated-button @click=${this._demoteTenant} ?disabled=${!this.project.id}>\n <md-icon slot=\"icon\">link_off</md-icon>테넌트 강등\n </md-elevated-button>`\n : html`<md-elevated-button green @click=${this._promoteTenant} ?disabled=${!this.project.id}>\n <md-icon slot=\"icon\">verified</md-icon>테넌트 승격\n </md-elevated-button>`}\n </span>\n </div>\n </div>\n\n\n </div>\n </div>\n `\n }\n\n async pageInitialized(lifecycle: PageLifecycle) {}\n\n async pageUpdated(changes: any, lifecycle: PageLifecycle) {\n if (!this.active) return\n\n this.projectId = await this._resolveProjectId(lifecycle.resourceId || '')\n await this.initProject(this.projectId)\n }\n\n /**\n * resourceId 를 정상 Project.id (UUID) 로 변환한다.\n * - UUID 형식이면 그대로 사용\n * - 그 외 (code 또는 비어있음) 에는 현재 테넌트 컨텍스트의 프로젝트 조회\n */\n private async _resolveProjectId(resourceId: string): Promise<string> {\n if (resourceId && resourceId.length === 36) return resourceId\n\n const response = await client.query({\n query: gql`\n query CurrentProject {\n currentProject {\n id\n }\n }\n `\n })\n\n return response.data?.currentProject?.id || ''\n }\n\n async initProject(projectId: string = '') {\n const response = await client.query({\n query: gql`\n query Project($id: String!, $filters: [Filter!]) {\n project(id: $id) {\n id\n code\n state\n tenantDomain {\n id\n subdomain\n name\n }\n name\n documentNaming\n sectorType\n buildingUsage\n postalCode\n geoGroup\n startDate\n endDate\n mainPhoto {\n fullpath\n }\n totalProgress\n weeklyProgress\n kpi\n inspPassRate\n robotProgressRate\n structuralSafetyRate\n robotCount\n buildingComplex {\n id\n address\n siteType\n latitude\n longitude\n area\n coverageRatio\n floorAreaRatio\n structureType\n workerCount\n designChangeCount\n plannedProgress\n actualProgress\n clientCompany\n constructionCompany\n supervisoryCompany\n designCompany\n drawing {\n id\n name\n }\n constructionType\n constructionCost\n etc\n notice\n householdCount\n buildingCount\n overallConstructorEmails\n taskConstructorEmails\n overallSupervisoryEmails\n taskSupervisoryEmails\n virtualTourLink\n buildings {\n id\n name\n floorCount\n hasBasement\n basementFloorCount\n }\n }\n }\n\n employees(filters: $filters) {\n items {\n id\n name\n jobResponsibility\n active\n user {\n id\n name\n email\n }\n }\n }\n }\n `,\n variables: {\n id: projectId,\n filters: [\n {\n name: 'active',\n operator: 'eq',\n value: true\n },\n {\n name: 'userId',\n operator: 'is_not_null',\n value: ''\n }\n ]\n },\n context: { headers: tenantHeaders((this.project as any)?.code) }\n })\n\n this.project = response.data?.project || { ...this.defaultProject }\n\n const items = response.data?.employees?.items || []\n this.overallConstructorList = this._filterUserByPermission(items, 'OVERALL_CONSTRUCTOR')\n this.taskConstructorList = this._filterUserByPermission(items, 'TASK_CONSTRUCTOR')\n this.overallSupervisoryList = this._filterUserByPermission(items, 'OVERALL_SUPERVISORY')\n this.taskSupervisoryList = this._filterUserByPermission(items, 'TASK_SUPERVISORY')\n\n this.updateContext()\n }\n\n private _filterUserByPermission(userList, permission: string) {\n return userList\n .filter(v => v.jobResponsibility == permission || v.jobResponsibility == 'ADMIN')\n .map(v => ({ name: v.name, value: v.user.email }))\n }\n\n private _stripTypename(obj: any): any {\n if (Array.isArray(obj)) {\n return obj.map(item => this._stripTypename(item))\n }\n if (obj && typeof obj === 'object' && !(obj instanceof File)) {\n const cleaned: any = {}\n for (const [key, value] of Object.entries(obj)) {\n if (key === '__typename') continue\n cleaned[key] = this._stripTypename(value)\n }\n return cleaned\n }\n return obj\n }\n\n private async _saveProject() {\n // 첨부 파일 필드 제거 (첨부 파일은 {filename}Upload 로 전송)\n delete this.project.mainPhoto\n delete this.project.buildingComplex.drawing\n // ProjectPatch 입력 타입에 없는 표시 전용 필드 제거 (테넌트 승격 UI 표시용)\n const projectCode = (this.project as any).code // 도메인 헤더 용으로 미리 보존\n delete (this.project as any).code\n delete (this.project as any).tenantDomain\n // state 는 raw 값('10'/'20')으로 로드되지만 ProjectPatch 는 enum 키 기대 — 일반\n // save 에서는 state 변경 의도 없으므로 patch 에서 제외. (state 변경은 _onUnfinalize 별도)\n delete (this.project as any).state\n\n // buildingComplex.siteType 가 SiteType enum 외 값 ('BUILDING' 같은 옛 잔재) 이면\n // mutation validation 실패. enum 에 없으면 null 로 정정.\n const validSiteTypes = new Set(Object.values(SiteType))\n if (\n this.project.buildingComplex?.siteType &&\n !validSiteTypes.has(this.project.buildingComplex.siteType as any)\n ) {\n this.project.buildingComplex.siteType = null as any\n }\n\n const response = await client.mutate({\n mutation: gql`\n mutation UpdateProject($project: ProjectPatch!) {\n updateProject(project: $project) {\n id\n }\n }\n `,\n variables: {\n project: this._stripTypename(this.project)\n },\n context: {\n hasUpload: true,\n headers: tenantHeaders(projectCode)\n }\n })\n\n if (!response.errors) {\n notify({ message: '저장에 성공하였습니다.' })\n }\n }\n\n /**\n * 완료(COMPLETED) 상태 프로젝트를 진행중(ONGOING) 으로 되돌림.\n * ProjectPatch 의 state 필드만 갱신 — 다른 폼 필드는 건드리지 않음.\n */\n private _onUnfinalize = async () => {\n if (!this.project?.id) return\n const ok = await OxPrompt.open({\n title: '완료 상태 해제',\n confirmButton: { text: '확인' },\n cancelButton: { text: '취소' }\n })\n if (!ok) return\n\n const response = await client.mutate({\n mutation: gql`\n mutation UpdateProject($project: ProjectPatch!) {\n updateProject(project: $project) {\n id\n state\n }\n }\n `,\n variables: { project: { id: this.project.id, state: 'ONGOING' } },\n context: { headers: tenantHeaders((this.project as any)?.code) }\n })\n\n if (!response.errors) {\n notify({ message: '진행중 상태로 변경되었습니다.' })\n await this.initProject(this.project.id)\n }\n }\n\n /**\n * 주소 옆 \"정보 연동\" 버튼 handler.\n * - 현재 폼의 주소를 backend 로 보냄 → backend 가 EnvVar 갱신 + 세움터 시나리오 호출\n * - 결과 (한글 raw + 숫자) 를 받아 BuildingComplex 폼 필드들에 매핑\n * · siteType (한글) → SiteType enum 변환 (BC.siteType, enum 컬럼)\n * · siteType (한글) → BC.constructionType 그대로 (자유 텍스트 컬럼)\n * · area / floorAreaRatio / coverageRatio / householdCount / buildingCount /\n * structureType → BC 동명 컬럼에 직접 대입\n * · upperFloorCount / lowerFloorCount / bldNm / useAprDay / pmsDay → BC 컬럼\n * 없음, 매핑 skip (참고용 콘솔 로그만)\n */\n private async _collectBasicInfo() {\n const address = (this.project?.buildingComplex?.address || '').trim()\n if (!address) {\n notify({ message: '먼저 프로젝트 주소를 입력해 주세요.' })\n return\n }\n this.collectingBasicInfo = true\n try {\n const response = await client.mutate({\n mutation: gql`\n mutation CollectProjectBasicInfo($projectId: String!, $address: String) {\n collectProjectBasicInfo(projectId: $projectId, address: $address) {\n source\n label\n ok\n message\n data\n }\n }\n `,\n variables: { projectId: this.project.id, address },\n context: { headers: tenantHeaders((this.project as any)?.code) }\n })\n if (response.errors) {\n throw new Error(response.errors.map((e: any) => e.message).join('\\n'))\n }\n const result = response.data?.collectProjectBasicInfo\n if (!result?.ok) {\n notify({ message: result?.message || '업데이트할 정보가 없습니다.' })\n return\n }\n const data = result.data || {}\n this._applyBasicInfoToForm(data)\n this.requestUpdate()\n notify({ message: '외부 시스템에서 기본 정보를 가져왔습니다. 검토 후 [저장]을 눌러주세요.' })\n } catch (err) {\n console.warn('[정보 연동] 실패', err)\n notify({ message: '외부 시스템 연동이 일시적으로 안 됩니다.' })\n } finally {\n this.collectingBasicInfo = false\n }\n }\n\n /**\n * 한글 주용도명 → SiteType enum.\n * - NFC normalize + 공백 제거로 보이지 않는 문자/표기 차이 대응\n * - 매핑 미존재시 null\n */\n private _siteTypeFromKorean(korean: string | null | undefined): string | null {\n if (!korean) return null\n const KEYS: Record<string, string> = {\n 공동주택: 'APARTMENT_COMPLEX',\n 단독주택: 'DETACHED_HOUSE',\n 제1종근린생활시설: 'NEIGHBORHOOD_FACILITY',\n 제2종근린생활시설: 'NEIGHBORHOOD_FACILITY',\n 근린생활시설: 'NEIGHBORHOOD_FACILITY',\n 업무시설: 'OFFICE',\n 판매시설: 'COMMERCIAL',\n 교육연구시설: 'EDUCATION',\n 의료시설: 'MEDICAL',\n 문화및집회시설: 'CULTURAL',\n 종교시설: 'RELIGIOUS',\n 공장: 'INDUSTRIAL',\n 창고시설: 'INDUSTRIAL',\n 운수시설: 'TRANSPORT'\n }\n const key = korean.normalize('NFC').replace(/\\s+/g, '').trim()\n return KEYS[key] || null\n }\n\n /**\n * 시나리오 result data 를 폼에 매핑 — BuildingComplex 와 Project 양쪽.\n * 빈 값(null/undefined/'')은 기존 값 유지 — 자동 수집이 사용자 입력을 무리하게 덮지 않도록.\n */\n private _applyBasicInfoToForm(data: Record<string, any>) {\n const bc = { ...this.project.buildingComplex }\n const setBC = (key: string, value: any) => {\n if (value === null || value === undefined || value === '') return\n ;(bc as any)[key] = value\n }\n setBC('area', this._toNum(data.area))\n setBC('floorAreaRatio', this._toNum(data.floorAreaRatio))\n setBC('coverageRatio', this._toNum(data.coverageRatio))\n setBC('householdCount', this._toNum(data.householdCount))\n setBC('buildingCount', this._toNum(data.buildingCount))\n setBC('structureType', data.structureType)\n // siteType: 한글 → SiteType enum 매핑. 매핑 안 되면 BC.siteType 빈 채로.\n if (data.siteType) {\n const enumVal = this._siteTypeFromKorean(data.siteType)\n if (enumVal) (bc as any).siteType = enumVal\n }\n\n // Project 엔티티 컬럼 — 착공일/준공일.\n // 세움터 useAprDay (사용승인일) = 준공으로 매핑. stcnsDay = 착공.\n const project: any = { ...this.project, buildingComplex: bc }\n if (data.startDate) project.startDate = data.startDate\n if (data.endDate) project.endDate = data.endDate\n this.project = project\n\n // 참고 메타 — BC/Project 직접 컬럼 없음, 콘솔로만 (permitDate=건축허가일, bldNm 등)\n console.log(\n '[정보 연동] meta:',\n JSON.stringify({\n upperFloorCount: data.upperFloorCount,\n lowerFloorCount: data.lowerFloorCount,\n bldNm: data.bldNm,\n permitDate: data.permitDate\n })\n )\n }\n\n private _toNum(v: any): number | null {\n if (v === null || v === undefined || v === '') return null\n const n = Number(v)\n return Number.isFinite(n) ? n : null\n }\n\n private async _promoteTenant() {\n if (!this.project.id) return\n\n if (!confirm('이 프로젝트를 테넌트로 승격하시겠습니까?\\n승격 시 관리번호(YYYY-NNNNN)가 자동 발번되며 변경할 수 없습니다.')) {\n return\n }\n\n const response = await client.mutate({\n mutation: gql`\n mutation PromoteProjectToTenant($projectId: String!) {\n promoteProjectToTenant(projectId: $projectId) {\n id\n code\n }\n }\n `,\n variables: { projectId: this.project.id }\n })\n\n if (!response.errors) {\n notify({ message: `테넌트로 승격되었습니다. 관리번호: ${response.data.promoteProjectToTenant.code}` })\n await this.initProject(this.projectId)\n }\n }\n\n private async _demoteTenant() {\n if (!this.project.id) return\n\n if (!confirm('이 프로젝트의 테넌트를 강등하시겠습니까?\\n관리번호는 보존되며 재승격 시 같은 번호가 재사용됩니다.')) {\n return\n }\n\n const response = await client.mutate({\n mutation: gql`\n mutation DemoteProjectTenant($projectId: String!) {\n demoteProjectTenant(projectId: $projectId)\n }\n `,\n variables: { projectId: this.project.id }\n })\n\n if (!response.errors) {\n notify({ message: '테넌트가 강등되었습니다.' })\n await this.initProject(this.projectId)\n }\n }\n\n // 동 적용 버튼을 누르면 입력한 수 만큼 해당 단지에 동 데이터 생성\n private _setBuilding() {\n const buildingCount: number = this.project?.buildingComplex?.buildingCount || 0\n const buildingInitData = { name: undefined, floorCount: undefined }\n\n // 빌딩 데이터가 없으면 빈 배열 넣어줌\n if (!this.project?.buildingComplex?.buildings?.length) {\n this.project.buildingComplex.buildings = []\n }\n\n if (this.project.buildingComplex.buildings.length >= buildingCount) {\n // 동 수가 더 작게 들어오면 기존 배열을 필요한 크기만큼 잘라내기\n this.project.buildingComplex.buildings = [...this.project.buildingComplex.buildings!.slice(0, buildingCount)]\n } else {\n // 동수가 더 크게 들어오면 기존 배열 + 빈 값을 채움\n const additionalCount = buildingCount - this.project.buildingComplex.buildings.length\n const additionalBuildings = Array.from({ length: additionalCount }, () => ({ ...buildingInitData }))\n this.project.buildingComplex.buildings = [...this.project.buildingComplex.buildings, ...additionalBuildings]\n }\n\n // 리렌더링\n this.project = { ...this.project }\n }\n\n // Input 요소의 값이 변경될 때 호출되는 콜백 함수\n /**\n * 우편번호 입력 핸들러 — postalCode 갱신과 동시에 앞 2자리 prefix 로 geoGroup\n * best-guess 자동 매핑. 매핑 안 되면 기존 값 유지.\n */\n private _onPostalCodeChange(event: InputEvent) {\n const target = event.target as HTMLInputElement\n const value = target.value\n ;(this.project as any).postalCode = value\n const inferred = inferGeoGroupFromPostalCode(value)\n if (inferred) (this.project as any).geoGroup = inferred\n }\n\n private _onInputChange(event: InputEvent, idx: number) {\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 if (target.hasAttribute('project')) {\n this.project[target.name] = inputVal\n\n // documentNaming 필드가 변경되면 실시간으로 미리보기 업데이트\n if (target.name === 'documentNaming') {\n this.requestUpdate()\n }\n } else if (target.hasAttribute('building-complex')) {\n this.project.buildingComplex![target.name] = inputVal\n } else if (target.hasAttribute('building')) {\n this.project.buildingComplex.buildings![idx][target.name] = inputVal\n }\n }\n\n // 이미지 업로드\n async onCreateAttachment(e: CustomEvent) {\n const target = e.target as HTMLInputElement\n const file = (target.name === 'mainPhoto' ? e.detail : e.detail[0]) || null\n\n if (target.name === 'mainPhoto') {\n this.project.mainPhotoUpload = file\n } else {\n this.project.buildingComplex.drawingUpload = file\n }\n }\n\n private _handleSelectionChange(e) {\n const name = e.target.getAttribute('name')\n const selectedValues = e.detail.selectedValues\n\n this.project.buildingComplex[name] = selectedValues\n }\n\n private _toggleBasement(idx: number) {\n const building = this.project.buildingComplex.buildings![idx]\n building.hasBasement = !building.hasBasement\n\n // 층 수가 0이거나 체크 해제시 지하층수 초기화 (플레이스 홀더 나오게 하기 위함)\n if (building.basementFloorCount == 0 || !building.hasBasement) {\n building.basementFloorCount = undefined\n }\n\n this.project.buildingComplex.buildings![idx]['hasBasement'] = building.hasBasement\n this.requestUpdate()\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"sv-project-update.js","sourceRoot":"","sources":["../../client/pages/sv-project-update.ts"],"names":[],"mappings":";AAAA,OAAO,4BAA4B,CAAA;AACnC,OAAO,yCAAyC,CAAA;AAChD,OAAO,gDAAgD,CAAA;AAEvD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAEzC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAA;AAC/B,OAAO,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAA;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,GAAG,MAAM,aAAa,CAAA;AAC7B,OAAO,EAAW,QAAQ,EAAqB,MAAM,sDAAsD,CAAA;AAC3G,OAAO,uDAAuD,CAAA;AAC9D,OAAO,mCAAmC,CAAA;AAC1C,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,iDAAiD,CAAA;AACpG,OAAO,EAAE,2BAA2B,EAAE,MAAM,6BAA6B,CAAA;AACzE,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAA;AAG1D,IAAM,eAAe,GAArB,MAAM,eAAgB,SAAQ,mBAAmB,CAAC,QAAQ,CAAC;IAA3D;;QAkTG,mBAAc,GAAG;YACvB,IAAI,EAAE,EAAE;YACR,cAAc,EAAE,EAAE;YAClB,eAAe,EAAE;gBACf,OAAO,EAAE,EAAE;gBACX,IAAI,EAAE,CAAC;gBACP,mBAAmB,EAAE,EAAE;gBACvB,aAAa,EAAE,EAAE;gBACjB,kBAAkB,EAAE,EAAE;gBACtB,aAAa,EAAE,EAAE;gBACjB,gBAAgB,EAAE,EAAE;gBACpB,SAAS,EAAE,EAAE;aACd;SACF,CAAA;QACQ,cAAS,GAAW,EAAE,CAAA;QACtB,YAAO,qBAAiB,IAAI,CAAC,cAAc,EAAE;QAC7C,mBAAc,GAAa,EAAE,CAAA;QAC7B,2BAAsB,GAA2C,EAAE,CAAA;QACnE,wBAAmB,GAA2C,EAAE,CAAA;QAChE,wBAAmB,GAA2C,EAAE,CAAA;QAChE,2BAAsB,GAA2C,EAAE,CAAA;QACnE,wBAAmB,GAAY,KAAK,CAAA;QA0f7C;;;WAGG;QACK,kBAAa,GAAG,KAAK,IAAI,EAAE;;YACjC,IAAI,CAAC,CAAA,MAAA,IAAI,CAAC,OAAO,0CAAE,EAAE,CAAA;gBAAE,OAAM;YAC7B,MAAM,EAAE,GAAG,MAAM,QAAQ,CAAC,IAAI,CAAC;gBAC7B,KAAK,EAAE,UAAU;gBACjB,aAAa,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;gBAC7B,YAAY,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;aAC7B,CAAC,CAAA;YACF,IAAI,CAAC,EAAE;gBAAE,OAAM;YAEf,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC;gBACnC,QAAQ,EAAE,GAAG,CAAA;;;;;;;OAOZ;gBACD,SAAS,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE;gBACjE,OAAO,EAAE,EAAE,OAAO,EAAE,aAAa,CAAC,MAAC,IAAI,CAAC,OAAe,0CAAE,IAAI,CAAC,EAAE;aACjE,CAAC,CAAA;YAEF,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;gBACrB,MAAM,CAAC,EAAE,OAAO,EAAE,kBAAkB,EAAE,CAAC,CAAA;gBACvC,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAA;YACzC,CAAC;QACH,CAAC,CAAA;IA4QH,CAAC;IAl0BC,IAAI,OAAO;;QACT,OAAO;YACL,KAAK,EACH,CAAC,mBAAmB,EAAE,KAAI,MAAA,IAAI,CAAC,OAAO,0CAAE,IAAI,CAAA;gBAC1C,CAAC,CAAC,gBAAgB,MAAA,IAAI,CAAC,OAAO,0CAAE,IAAI,EAAE;gBACtC,CAAC,CAAC,YAAY;SACnB,CAAA;IACH,CAAC;IAyBD,MAAM;;QACJ,OAAO,IAAI,CAAA;;qBAEM,IAAI,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE;wBACjB,IAAI,CAAC,OAAe,CAAC,KAAK,IAAI,EAAE;;wBAEjC,IAAI,CAAC,YAAY;sBACnB,IAAI,CAAC,aAAa;;;;;;;;;;;;;yBAaf,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE;yBACvB,IAAI,CAAC,cAAc;;;;;;;;;;;yBAWnB,CAAA,MAAA,MAAA,IAAI,CAAC,OAAO,0CAAE,eAAe,0CAAE,OAAO,KAAI,EAAE;yBAC5C,IAAI,CAAC,cAAc;;;;4BAIhB,IAAI,CAAC,mBAAmB;yBAC3B,GAAG,EAAE,CAAC,IAAI,CAAC,iBAAiB,EAAE;;;kBAGrC,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU;;;;;;;;;;;yBAWvC,IAAI,CAAC,OAAe,CAAC,UAAU,IAAI,EAAE;yBACtC,IAAI,CAAC,mBAAmB;;;;;;;;;;;;yBAYxB,CAAA,MAAA,MAAA,MAAA,IAAI,CAAC,OAAO,0CAAE,eAAe,0CAAE,IAAI,0CAAE,QAAQ,EAAE,KAAI,EAAE;yBACrD,IAAI,CAAC,cAAc;;;;;;;;;;;;;;yBAcnB,CAAA,MAAA,MAAA,MAAA,IAAI,CAAC,OAAO,0CAAE,eAAe,0CAAE,cAAc,0CAAE,QAAQ,EAAE,KAAI,EAAE;yBAC/D,IAAI,CAAC,cAAc;;;;;;;;;;;;;yBAanB,IAAI,CAAC,OAAO,CAAC,SAAS,IAAI,EAAE;yBAC5B,IAAI,CAAC,cAAc;;;;;;;;yBAQnB,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE;yBAC1B,IAAI,CAAC,cAAc;;;;;;;;;qEASyB,IAAI,CAAC,cAAc;8CAC1C,CAAE,IAAI,CAAC,OAAe,CAAC,UAAU;;;8CAGhC,IAAI,CAAC,OAAe,CAAC,UAAU,KAAK,QAAQ;;;8CAG5C,IAAI,CAAC,OAAe,CAAC,UAAU,KAAK,SAAS;;;;;;;;;wEASpB,IAAI,CAAC,cAAc;8CAC7C,CAAE,IAAI,CAAC,OAAe,CAAC,aAAa;;;;8BAInD,IAAI,CAAC,OAAe,CAAC,aAAa,KAAK,aAAa;;;;;;8BAMpD,IAAI,CAAC,OAAe,CAAC,aAAa,KAAK,iBAAiB;;;;;;;;;;;;;;;;yBAgB9D,CAAA,MAAA,MAAA,IAAI,CAAC,OAAO,0CAAE,eAAe,0CAAE,aAAa,KAAI,EAAE;yBAClD,IAAI,CAAC,cAAc;;;;;;;;;;;yBAWnB,CAAA,MAAA,MAAA,IAAI,CAAC,OAAO,0CAAE,eAAe,0CAAE,mBAAmB,KAAI,EAAE;yBACxD,IAAI,CAAC,cAAc;;;;;;;;;;;yBAWnB,CAAA,MAAA,MAAA,IAAI,CAAC,OAAO,0CAAE,eAAe,0CAAE,aAAa,KAAI,EAAE;yBAClD,IAAI,CAAC,cAAc;;;;;;;;;;;yBAWnB,CAAA,MAAA,MAAA,IAAI,CAAC,OAAO,0CAAE,eAAe,0CAAE,kBAAkB,KAAI,EAAE;yBACvD,IAAI,CAAC,cAAc;;;;;;;;;;wBAUpB,CAAA,MAAA,MAAA,IAAI,CAAC,OAAO,0CAAE,SAAS,0CAAE,QAAQ,KAAI,EAAE;0BACrC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC;;;;;;;;;;;;yBAYnC,CAAA,MAAA,MAAA,MAAA,IAAI,CAAC,OAAO,0CAAE,eAAe,0CAAE,gBAAgB,0CAAE,QAAQ,EAAE,KAAI,EAAE;yBACjE,IAAI,CAAC,cAAc;;;;;;;;;;;;;yBAanB,CAAA,MAAA,MAAA,MAAA,IAAI,CAAC,OAAO,0CAAE,eAAe,0CAAE,WAAW,0CAAE,QAAQ,EAAE,KAAI,EAAE;yBAC5D,IAAI,CAAC,cAAc;;;;;;;;;;;;;kBAa1B,IAAI,CAAC,OAAO,CAAC,YAAY;YACzB,CAAC,CAAC,IAAI,CAAA,WAAW,YAAY,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG;yBAC1C,IAAI,CAAC,OAAO,CAAC,IAAI;sBACpB;YACJ,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI;gBACjB,CAAC,CAAC,IAAI,CAAA,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE;gBAC5B,CAAC,CAAC,IAAI,CAAA,kDAAkD;;;;;;kBAM1D,IAAI,CAAC,OAAO,CAAC,YAAY;YACzB,CAAC,CAAC,IAAI,CAAA,qCAAqC;YAC3C,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI;gBACjB,CAAC,CAAC,IAAI,CAAA,2CAA2C,IAAI,CAAC,OAAO,CAAC,IAAI,OAAO;gBACzE,CAAC,CAAC,IAAI,CAAA,+BAA+B;;;;;;kBAMvC,IAAI,CAAC,OAAO,CAAC,YAAY;YACzB,CAAC,CAAC,IAAI,CAAA,8BAA8B,IAAI,CAAC,aAAa,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE;;0CAE5D;YACxB,CAAC,CAAC,IAAI,CAAA,oCAAoC,IAAI,CAAC,cAAc,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE;;0CAEnE;;;;;;;;KAQrC,CAAA;IACH,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,SAAwB,IAAG,CAAC;IAElD,KAAK,CAAC,WAAW,CAAC,OAAY,EAAE,SAAwB;QACtD,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAM;QAExB,IAAI,CAAC,SAAS,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,SAAS,CAAC,UAAU,IAAI,EAAE,CAAC,CAAA;QACzE,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;IACxC,CAAC;IAED;;;;OAIG;IACK,KAAK,CAAC,iBAAiB,CAAC,UAAkB;;QAChD,IAAI,UAAU,IAAI,UAAU,CAAC,MAAM,KAAK,EAAE;YAAE,OAAO,UAAU,CAAA;QAE7D,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC;YAClC,KAAK,EAAE,GAAG,CAAA;;;;;;OAMT;SACF,CAAC,CAAA;QAEF,OAAO,CAAA,MAAA,MAAA,QAAQ,CAAC,IAAI,0CAAE,cAAc,0CAAE,EAAE,KAAI,EAAE,CAAA;IAChD,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,YAAoB,EAAE;;QACtC,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC;YAClC,KAAK,EAAE,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAsFT;YACD,SAAS,EAAE;gBACT,EAAE,EAAE,SAAS;gBACb,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,QAAQ;wBACd,QAAQ,EAAE,IAAI;wBACd,KAAK,EAAE,IAAI;qBACZ;oBACD;wBACE,IAAI,EAAE,QAAQ;wBACd,QAAQ,EAAE,aAAa;wBACvB,KAAK,EAAE,EAAE;qBACV;iBACF;aACF;YACD,OAAO,EAAE,EAAE,OAAO,EAAE,aAAa,CAAC,MAAC,IAAI,CAAC,OAAe,0CAAE,IAAI,CAAC,EAAE;SACjE,CAAC,CAAA;QAEF,IAAI,CAAC,OAAO,GAAG,CAAA,MAAA,QAAQ,CAAC,IAAI,0CAAE,OAAO,uBAAS,IAAI,CAAC,cAAc,CAAE,CAAA;QAEnE,MAAM,KAAK,GAAG,CAAA,MAAA,MAAA,QAAQ,CAAC,IAAI,0CAAE,SAAS,0CAAE,KAAK,KAAI,EAAE,CAAA;QACnD,IAAI,CAAC,sBAAsB,GAAG,IAAI,CAAC,uBAAuB,CAAC,KAAK,EAAE,qBAAqB,CAAC,CAAA;QACxF,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC,uBAAuB,CAAC,KAAK,EAAE,kBAAkB,CAAC,CAAA;QAClF,IAAI,CAAC,sBAAsB,GAAG,IAAI,CAAC,uBAAuB,CAAC,KAAK,EAAE,qBAAqB,CAAC,CAAA;QACxF,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAC,uBAAuB,CAAC,KAAK,EAAE,kBAAkB,CAAC,CAAA;QAElF,IAAI,CAAC,aAAa,EAAE,CAAA;IACtB,CAAC;IAEO,uBAAuB,CAAC,QAAQ,EAAE,UAAkB;QAC1D,OAAO,QAAQ;aACZ,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,iBAAiB,IAAI,UAAU,IAAI,CAAC,CAAC,iBAAiB,IAAI,OAAO,CAAC;aAChF,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAA;IACtD,CAAC;IAEO,cAAc,CAAC,GAAQ;QAC7B,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YACvB,OAAO,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAA;QACnD,CAAC;QACD,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,CAAC,GAAG,YAAY,IAAI,CAAC,EAAE,CAAC;YAC7D,MAAM,OAAO,GAAQ,EAAE,CAAA;YACvB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC/C,IAAI,GAAG,KAAK,YAAY;oBAAE,SAAQ;gBAClC,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAA;YAC3C,CAAC;YACD,OAAO,OAAO,CAAA;QAChB,CAAC;QACD,OAAO,GAAG,CAAA;IACZ,CAAC;IAEO,KAAK,CAAC,YAAY;;QACxB,6CAA6C;QAC7C,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,CAAA;QAC7B,OAAO,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,OAAO,CAAA;QAC3C,qDAAqD;QACrD,MAAM,WAAW,GAAI,IAAI,CAAC,OAAe,CAAC,IAAI,CAAA,CAAE,mBAAmB;QACnE,OAAQ,IAAI,CAAC,OAAe,CAAC,IAAI,CAAA;QACjC,OAAQ,IAAI,CAAC,OAAe,CAAC,YAAY,CAAA;QACzC,iEAAiE;QACjE,sEAAsE;QACtE,OAAQ,IAAI,CAAC,OAAe,CAAC,KAAK,CAAA;QAElC,uEAAuE;QACvE,gDAAgD;QAChD,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAA;QACvD,IACE,CAAA,MAAA,IAAI,CAAC,OAAO,CAAC,eAAe,0CAAE,QAAQ;YACtC,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,QAAe,CAAC,EACjE,CAAC;YACD,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,QAAQ,GAAG,IAAW,CAAA;QACrD,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC;YACnC,QAAQ,EAAE,GAAG,CAAA;;;;;;OAMZ;YACD,SAAS,EAAE;gBACT,OAAO,EAAE,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC;aAC3C;YACD,OAAO,EAAE;gBACP,SAAS,EAAE,IAAI;gBACf,OAAO,EAAE,aAAa,CAAC,WAAW,CAAC;aACpC;SACF,CAAC,CAAA;QAEF,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;YACrB,+DAA+D;YAC/D,oEAAoE;YACpE,sCAAsC;YACtC,IAAI,CAAC;gBACH,MAAM,oBAAoB,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAA;YACvD,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,OAAO,CAAC,IAAI,CAAC,mCAAmC,EAAE,CAAC,CAAC,CAAA;YACtD,CAAC;YACD,MAAM,CAAC,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC,CAAA;QACrC,CAAC;IACH,CAAC;IAkCD;;;;;;;;;;OAUG;IACK,KAAK,CAAC,iBAAiB;;QAC7B,MAAM,OAAO,GAAG,CAAC,CAAA,MAAA,MAAA,IAAI,CAAC,OAAO,0CAAE,eAAe,0CAAE,OAAO,KAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;QACrE,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,CAAC,EAAE,OAAO,EAAE,sBAAsB,EAAE,CAAC,CAAA;YAC3C,OAAM;QACR,CAAC;QACD,IAAI,CAAC,mBAAmB,GAAG,IAAI,CAAA;QAC/B,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC;gBACnC,QAAQ,EAAE,GAAG,CAAA;;;;;;;;;;SAUZ;gBACD,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,EAAE;gBAClD,OAAO,EAAE,EAAE,OAAO,EAAE,aAAa,CAAC,MAAC,IAAI,CAAC,OAAe,0CAAE,IAAI,CAAC,EAAE;aACjE,CAAC,CAAA;YACF,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;gBACpB,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;YACxE,CAAC;YACD,MAAM,MAAM,GAAG,MAAA,QAAQ,CAAC,IAAI,0CAAE,uBAAuB,CAAA;YACrD,IAAI,CAAC,CAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,EAAE,CAAA,EAAE,CAAC;gBAChB,MAAM,CAAC,EAAE,OAAO,EAAE,CAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,OAAO,KAAI,iBAAiB,EAAE,CAAC,CAAA;gBACzD,OAAM;YACR,CAAC;YACD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,EAAE,CAAA;YAC9B,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAA;YAChC,IAAI,CAAC,aAAa,EAAE,CAAA;YACpB,MAAM,CAAC,EAAE,OAAO,EAAE,2CAA2C,EAAE,CAAC,CAAA;QAClE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,YAAY,EAAE,GAAG,CAAC,CAAA;YAC/B,MAAM,CAAC,EAAE,OAAO,EAAE,yBAAyB,EAAE,CAAC,CAAA;QAChD,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,mBAAmB,GAAG,KAAK,CAAA;QAClC,CAAC;IACH,CAAC;IAED;;;;OAIG;IACK,mBAAmB,CAAC,MAAiC;QAC3D,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAA;QACxB,MAAM,IAAI,GAA2B;YACnC,IAAI,EAAE,mBAAmB;YACzB,IAAI,EAAE,gBAAgB;YACtB,SAAS,EAAE,uBAAuB;YAClC,SAAS,EAAE,uBAAuB;YAClC,MAAM,EAAE,uBAAuB;YAC/B,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,YAAY;YAClB,MAAM,EAAE,WAAW;YACnB,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,UAAU;YACnB,IAAI,EAAE,WAAW;YACjB,EAAE,EAAE,YAAY;YAChB,IAAI,EAAE,YAAY;YAClB,IAAI,EAAE,WAAW;SAClB,CAAA;QACD,MAAM,GAAG,GAAG,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;QAC9D,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,CAAA;IAC1B,CAAC;IAED;;;OAGG;IACK,qBAAqB,CAAC,IAAyB;QACrD,MAAM,EAAE,qBAAQ,IAAI,CAAC,OAAO,CAAC,eAAe,CAAE,CAAA;QAC9C,MAAM,KAAK,GAAG,CAAC,GAAW,EAAE,KAAU,EAAE,EAAE;YACxC,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE;gBAAE,OAC1D;YAAC,EAAU,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;QAC3B,CAAC,CAAA;QACD,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;QACrC,KAAK,CAAC,gBAAgB,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAA;QACzD,KAAK,CAAC,eAAe,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAA;QACvD,KAAK,CAAC,gBAAgB,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAA;QACzD,KAAK,CAAC,eAAe,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAA;QACvD,KAAK,CAAC,eAAe,EAAE,IAAI,CAAC,aAAa,CAAC,CAAA;QAC1C,6DAA6D;QAC7D,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,MAAM,OAAO,GAAG,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;YACvD,IAAI,OAAO;gBAAG,EAAU,CAAC,QAAQ,GAAG,OAAO,CAAA;QAC7C,CAAC;QAED,4BAA4B;QAC5B,kDAAkD;QAClD,MAAM,OAAO,mCAAa,IAAI,CAAC,OAAO,KAAE,eAAe,EAAE,EAAE,GAAE,CAAA;QAC7D,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAA;QACtD,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAA;QAChD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QAEtB,gEAAgE;QAChE,OAAO,CAAC,GAAG,CACT,eAAe,EACf,IAAI,CAAC,SAAS,CAAC;YACb,eAAe,EAAE,IAAI,CAAC,eAAe;YACrC,eAAe,EAAE,IAAI,CAAC,eAAe;YACrC,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,UAAU,EAAE,IAAI,CAAC,UAAU;SAC5B,CAAC,CACH,CAAA;IACH,CAAC;IAEO,MAAM,CAAC,CAAM;QACnB,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,EAAE;YAAE,OAAO,IAAI,CAAA;QAC1D,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAA;QACnB,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IACtC,CAAC;IAEO,KAAK,CAAC,cAAc;QAC1B,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE;YAAE,OAAM;QAE5B,IAAI,CAAC,OAAO,CAAC,oEAAoE,CAAC,EAAE,CAAC;YACnF,OAAM;QACR,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC;YACnC,QAAQ,EAAE,GAAG,CAAA;;;;;;;OAOZ;YACD,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE;SAC1C,CAAC,CAAA;QAEF,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;YACrB,MAAM,CAAC,EAAE,OAAO,EAAE,uBAAuB,QAAQ,CAAC,IAAI,CAAC,sBAAsB,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;YACvF,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QACxC,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,aAAa;QACzB,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE;YAAE,OAAM;QAE5B,IAAI,CAAC,OAAO,CAAC,yDAAyD,CAAC,EAAE,CAAC;YACxE,OAAM;QACR,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC;YACnC,QAAQ,EAAE,GAAG,CAAA;;;;OAIZ;YACD,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE;SAC1C,CAAC,CAAA;QAEF,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;YACrB,MAAM,CAAC,EAAE,OAAO,EAAE,eAAe,EAAE,CAAC,CAAA;YACpC,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QACxC,CAAC;IACH,CAAC;IAED,wCAAwC;IAChC,YAAY;;QAClB,MAAM,aAAa,GAAW,CAAA,MAAA,MAAA,IAAI,CAAC,OAAO,0CAAE,eAAe,0CAAE,aAAa,KAAI,CAAC,CAAA;QAC/E,MAAM,gBAAgB,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,CAAA;QAEnE,uBAAuB;QACvB,IAAI,CAAC,CAAA,MAAA,MAAA,MAAA,IAAI,CAAC,OAAO,0CAAE,eAAe,0CAAE,SAAS,0CAAE,MAAM,CAAA,EAAE,CAAC;YACtD,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,SAAS,GAAG,EAAE,CAAA;QAC7C,CAAC;QAED,IAAI,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,SAAS,CAAC,MAAM,IAAI,aAAa,EAAE,CAAC;YACnE,sCAAsC;YACtC,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,SAAS,GAAG,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,SAAU,CAAC,KAAK,CAAC,CAAC,EAAE,aAAa,CAAC,CAAC,CAAA;QAC/G,CAAC;aAAM,CAAC;YACN,gCAAgC;YAChC,MAAM,eAAe,GAAG,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,SAAS,CAAC,MAAM,CAAA;YACrF,MAAM,mBAAmB,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,eAAe,EAAE,EAAE,GAAG,EAAE,CAAC,mBAAM,gBAAgB,EAAG,CAAC,CAAA;YACpG,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,SAAS,GAAG,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,SAAS,EAAE,GAAG,mBAAmB,CAAC,CAAA;QAC9G,CAAC;QAED,OAAO;QACP,IAAI,CAAC,OAAO,qBAAQ,IAAI,CAAC,OAAO,CAAE,CAAA;IACpC,CAAC;IAED,gCAAgC;IAChC;;;OAGG;IACK,mBAAmB,CAAC,KAAiB;QAC3C,MAAM,MAAM,GAAG,KAAK,CAAC,MAA0B,CAAA;QAC/C,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CACzB;QAAC,IAAI,CAAC,OAAe,CAAC,UAAU,GAAG,KAAK,CAAA;QACzC,MAAM,QAAQ,GAAG,2BAA2B,CAAC,KAAK,CAAC,CAAA;QACnD,IAAI,QAAQ;YAAG,IAAI,CAAC,OAAe,CAAC,QAAQ,GAAG,QAAQ,CAAA;IACzD,CAAC;IAEO,cAAc,CAAC,KAAiB,EAAE,GAAW;QACnD,MAAM,MAAM,GAAG,KAAK,CAAC,MAA0B,CAAA;QAC/C,IAAI,QAAQ,GAAQ,MAAM,CAAC,KAAK,CAAA;QAEhC,qBAAqB;QACrB,IAAI,MAAM,CAAC,YAAY,CAAC,SAAS,CAAC,EAAE,CAAC;YACnC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC,CAAA;QACpD,CAAC;QAED,IAAI,MAAM,CAAC,YAAY,CAAC,SAAS,CAAC,EAAE,CAAC;YACnC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAA;YAEpC,0CAA0C;YAC1C,IAAI,MAAM,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;gBACrC,IAAI,CAAC,aAAa,EAAE,CAAA;YACtB,CAAC;QACH,CAAC;aAAM,IAAI,MAAM,CAAC,YAAY,CAAC,kBAAkB,CAAC,EAAE,CAAC;YACnD,IAAI,CAAC,OAAO,CAAC,eAAgB,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAA;QACvD,CAAC;aAAM,IAAI,MAAM,CAAC,YAAY,CAAC,UAAU,CAAC,EAAE,CAAC;YAC3C,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,SAAU,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAA;QACtE,CAAC;IACH,CAAC;IAED,UAAU;IACV,KAAK,CAAC,kBAAkB,CAAC,CAAc;QACrC,MAAM,MAAM,GAAG,CAAC,CAAC,MAA0B,CAAA;QAC3C,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,CAAA;QAE3E,IAAI,MAAM,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;YAChC,IAAI,CAAC,OAAO,CAAC,eAAe,GAAG,IAAI,CAAA;QACrC,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,aAAa,GAAG,IAAI,CAAA;QACnD,CAAC;IACH,CAAC;IAEO,sBAAsB,CAAC,CAAC;QAC9B,MAAM,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,CAAA;QAC1C,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC,cAAc,CAAA;QAE9C,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,GAAG,cAAc,CAAA;IACrD,CAAC;IAEO,eAAe,CAAC,GAAW;QACjC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,SAAU,CAAC,GAAG,CAAC,CAAA;QAC7D,QAAQ,CAAC,WAAW,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAA;QAE5C,gDAAgD;QAChD,IAAI,QAAQ,CAAC,kBAAkB,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;YAC9D,QAAQ,CAAC,kBAAkB,GAAG,SAAS,CAAA;QACzC,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,SAAU,CAAC,GAAG,CAAC,CAAC,aAAa,CAAC,GAAG,QAAQ,CAAC,WAAW,CAAA;QAClF,IAAI,CAAC,aAAa,EAAE,CAAA;IACtB,CAAC;;AAzmCM,sBAAM,GAAG;IACd,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAoSF;CACF,AAtSY,CAsSZ;AAyBQ;IAAR,KAAK,EAAE;;kDAAuB;AACtB;IAAR,KAAK,EAAE;;gDAA8C;AAC7C;IAAR,KAAK,EAAE;;uDAA8B;AAC7B;IAAR,KAAK,EAAE;8BAAyB,KAAK;+DAAsC;AACnE;IAAR,KAAK,EAAE;8BAAsB,KAAK;4DAAsC;AAChE;IAAR,KAAK,EAAE;8BAAsB,KAAK;4DAAsC;AAChE;IAAR,KAAK,EAAE;8BAAyB,KAAK;+DAAsC;AACnE;IAAR,KAAK,EAAE;;4DAAqC;AAvUlC,eAAe;IAD3B,aAAa,CAAC,mBAAmB,CAAC;GACtB,eAAe,CA2mC3B","sourcesContent":["import '@material/web/icon/icon.js'\nimport '@material/web/button/elevated-button.js'\nimport '@material/web/textfield/outlined-text-field.js'\n\nimport { PageView } from '@operato/shell'\nimport { PageLifecycle } from '@operato/shell/dist/src/app/pages/page-view'\nimport { css, html } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\nimport { ScopedElementsMixin } from '@open-wc/scoped-elements'\nimport { client } from '@operato/graphql'\nimport { notify } from '@operato/layout'\nimport { OxPrompt } from '@operato/popup'\nimport gql from 'graphql-tag'\nimport { Project, SiteType, SITE_TYPE_DISPLAY } from '@dssp/project/dist-client/pages/project/project-list'\nimport '@dssp/project/dist-client/pages/lib/select2-component'\nimport './component/project-update-header'\nimport { isProjectTypeDomain, tenantHeaders } from '@dssp/project/dist-client/shared/domain-context'\nimport { inferGeoGroupFromPostalCode } from '../shared/geo-group-mapping'\nimport { syncProjectInfoToKpi } from '../shared/kpi-project-sync'\n\n@customElement('sv-project-update')\nexport class SvProjectUpdate extends ScopedElementsMixin(PageView) {\n static styles = [\n css`\n :host {\n display: grid;\n grid-template-rows: 55px auto;\n color: #4e5055;\n\n width: 100%;\n background-color: var(--md-sys-color-background, #f6f6f6);\n overflow-y: auto;\n\n --grid-record-emphasized-background-color: red;\n --grid-record-emphasized-color: yellow;\n }\n\n md-outlined-text-field {\n width: 100%;\n\n --md-outlined-text-field-container-shape: 5px;\n --md-outlined-text-field-outline-color: rgba(51, 51, 51, 0.2);\n --md-outlined-text-field-focus-outline-color: #1f7fd9;\n --md-outlined-text-field-focus-outline-width: 1px;\n --md-sys-color-primary: #586878;\n --md-outlined-text-field-input-text-size: 14px;\n --md-outlined-field-bottom-space: 3px;\n --md-outlined-field-top-space: 3px;\n --md-outlined-field-leading-space: var(--spacing-medium, 8px);\n --md-outlined-field-trailing-space: var(--spacing-medium, 8px);\n }\n select2-component {\n width: 100%;\n }\n md-filled-select {\n width: 100%;\n --md-filled-select-text-field-container-color: transparent;\n --md-filled-select-text-field-active-indicator-color: #999;\n --md-filled-select-text-field-input-text-size: 14px;\n --md-filled-select-text-field-input-text-line-height: 6px;\n }\n input[type='date'] {\n border: 1px solid rgba(51, 51, 51, 0.2);\n padding: var(--spacing-small, 4px) var(--spacing-medium, 8px);\n border-radius: 5px;\n }\n md-outlined-text-field[type='textarea'] {\n width: 100%;\n height: 120px;\n resize: none;\n }\n\n ox-input-image {\n width: 120px;\n height: 90px;\n }\n ox-input-file {\n width: 120px;\n height: 90px;\n padding: 0;\n }\n\n div[body] {\n display: flex;\n margin: var(--spacing-large, 12px);\n margin-top: 0;\n gap: var(--spacing-medium, 8px);\n\n & > div {\n display: flex;\n height: fit-content;\n flex-direction: column;\n padding: var(--spacing-large, 12px);\n background-color: var(--md-sys-color-on-primary);\n border: 1px solid #cccccc80;\n border-radius: 5px;\n gap: var(--spacing-medium, 8px);\n\n h3 {\n color: #2e79be;\n font-size: 16px;\n margin: 0px;\n }\n\n div[row] {\n display: flex;\n align-items: center;\n\n span:first-child {\n flex: 0.3;\n text-align: right;\n font-size: 14px;\n }\n\n span:last-child {\n flex: 0.7;\n display: flex;\n gap: var(--spacing-medium, 8px);\n margin-left: var(--spacing-medium, 8px);\n align-items: center;\n\n &.address-row {\n md-outlined-text-field {\n flex: 1;\n }\n .info-link-btn {\n flex: 0 0 auto;\n --md-elevated-button-container-color: #2e79be;\n --md-elevated-button-label-text-color: #fff;\n --md-elevated-button-container-height: 36px;\n }\n }\n\n &[align-end] {\n align-items: end;\n }\n }\n }\n }\n\n div[project-info] {\n flex: 0.55;\n }\n\n div[document-preview] {\n width: 100%;\n font-size: 12px;\n color: #2e79be;\n font-weight: 500;\n background-color: #f8f9fa;\n padding: 8px 12px;\n border-radius: 4px;\n border-left: 3px solid #2e79be;\n margin-top: 4px;\n }\n\n div[detail-info] {\n flex: 0.45;\n padding: 0px;\n gap: 10px;\n background-color: transparent;\n border: none;\n\n & > div {\n display: flex;\n flex-direction: column;\n gap: var(--spacing-medium, 8px);\n border: 1px solid #cccccc80;\n border-radius: 5px;\n padding: var(--spacing-large, 12px);\n background-color: var(--md-sys-color-on-primary);\n\n md-outlined-text-field[type='text'] {\n width: 60%;\n }\n\n md-elevated-button {\n --md-elevated-button-container-height: 30px;\n --md-elevated-button-container-color: var(--md-sys-color-on-primary);\n --md-elevated-button-label-text-size: 16px;\n }\n hr {\n border: 1px rgba(51, 51, 51, 0.1) dashed;\n width: 100%;\n margin-bottom: var(--spacing-tiny, 2px);\n }\n div[warn] {\n font-size: 12px;\n color: red;\n margin-left: var(--spacing-small, 4px);\n margin-bottom: var(--spacing-small, 4px);\n }\n\n md-outlined-button {\n min-height: 30px;\n padding: 0 var(--spacing-large, 12px);\n box-shadow: 1px 1px 1px #0000001a;\n --md-outlined-button-label-text-color: #586878;\n --md-outlined-button-label-text-weight: bold;\n --md-sys-color-outline: rgba(51, 51, 51, 0.2);\n }\n\n div[row] {\n span:first-child {\n flex: 0.2;\n }\n span:last-child {\n flex: 0.8;\n }\n }\n\n div[separate-container] {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n gap: 8px 30px;\n margin-left: 15px;\n\n md-outlined-text-field {\n width: 80px;\n }\n\n & > div {\n display: flex;\n gap: 15px;\n\n span[floor-row] {\n display: inline-flex;\n align-items: center;\n margin-left: 5px;\n }\n\n span[floor-title] {\n min-width: 33px;\n margin-right: var(--spacing-small, 4px);\n }\n span:first-child {\n flex: 0.4;\n justify-content: flex-start;\n display: flex;\n }\n span:last-child {\n flex: 0.6;\n display: flex;\n align-items: center;\n padding-left: var(--spacing-medium, 8px);\n }\n }\n\n div[basement-container] {\n display: flex;\n align-items: center;\n gap: 4px;\n\n div[basement-checkbox] {\n display: flex;\n align-items: center;\n cursor: pointer;\n\n md-icon {\n --md-icon-size: 25px;\n color: #808080;\n transition: opacity 0.2s ease;\n }\n\n &[disabled] {\n cursor: not-allowed;\n opacity: 0.4;\n\n md-icon {\n color: #cccccc;\n }\n }\n\n &[checked] md-icon {\n background-color: #2e79be;\n color: white;\n border-radius: 3px;\n }\n }\n\n md-outlined-text-field[basement-floor] {\n width: 70px;\n margin-left: var(--spacing-small, 4px);\n opacity: 1;\n transition: opacity 0.2s ease;\n\n &[disabled] {\n opacity: 0.4;\n pointer-events: none;\n }\n }\n }\n }\n\n &[project] {\n div[separate-container] {\n margin-left: 15px;\n\n & > div {\n align-items: center;\n\n span:first-child {\n flex: 0.4;\n min-width: 100px;\n }\n span:last-child {\n flex: 0.4;\n margin-left: 0;\n }\n }\n }\n }\n }\n }\n }\n `\n ]\n\n get context() {\n return {\n title:\n !isProjectTypeDomain() && this.project?.name\n ? `프로젝트 정보 관리 - ${this.project?.name}`\n : '프로젝트 정보 관리'\n }\n }\n\n private defaultProject = {\n name: '',\n documentNaming: '',\n buildingComplex: {\n address: '',\n area: 0,\n constructionCompany: '',\n clientCompany: '',\n supervisoryCompany: '',\n designCompany: '',\n constructionType: '',\n buildings: []\n }\n }\n @state() projectId: string = ''\n @state() project: Project = { ...this.defaultProject }\n @state() selectedValues: string[] = []\n @state() overallConstructorList: Array<{ name: string; value: string }> = []\n @state() taskConstructorList: Array<{ name: string; value: string }> = []\n @state() taskSupervisoryList: Array<{ name: string; value: string }> = []\n @state() overallSupervisoryList: Array<{ name: string; value: string }> = []\n @state() collectingBasicInfo: boolean = false\n\n render() {\n return html`\n <dkpi-project-update-header\n .projectId=${this.project.id || ''}\n .projectState=${(this.project as any).state || ''}\n title=\"프로젝트 정보 관리\"\n @custom-click=${this._saveProject}\n @unfinalize=${this._onUnfinalize}\n ></dkpi-project-update-header>\n\n <div body>\n <div project-info>\n <h3>기본 정보</h3>\n <div row>\n <span>프로젝트명</span>\n <span\n ><md-outlined-text-field\n type=\"text\"\n name=\"name\"\n project\n .value=${this.project.name || ''}\n @input=${this._onInputChange}\n ></md-outlined-text-field>\n </span>\n </div>\n <div row>\n <span>프로젝트 주소</span>\n <span class=\"address-row\">\n <md-outlined-text-field\n type=\"text\"\n name=\"address\"\n building-complex\n .value=${this.project?.buildingComplex?.address || ''}\n @input=${this._onInputChange}\n ></md-outlined-text-field>\n <md-elevated-button\n class=\"info-link-btn\"\n ?disabled=${this.collectingBasicInfo}\n @click=${() => this._collectBasicInfo()}\n title=\"입력된 주소로 세움터 건축물대장 정보를 가져와 폼을 채웁니다\"\n >\n ${this.collectingBasicInfo ? '연동 중…' : '🔗 정보 연동'}\n </md-elevated-button>\n </span>\n </div>\n <div row>\n <span>우편번호</span>\n <span>\n <md-outlined-text-field\n type=\"text\"\n name=\"postalCode\"\n project\n .value=${(this.project as any).postalCode || ''}\n @input=${this._onPostalCodeChange}\n ></md-outlined-text-field>\n </span>\n </div>\n <div row>\n <span>연 면적</span>\n <span align-end\n ><md-outlined-text-field\n type=\"text\"\n name=\"area\"\n numeric\n building-complex\n .value=${this.project?.buildingComplex?.area?.toString() || ''}\n @input=${this._onInputChange}\n suffix-text=\"㎡\"\n ></md-outlined-text-field>\n </span>\n </div>\n\n <div row>\n <span>용적률</span>\n <span align-end>\n <md-outlined-text-field\n type=\"text\"\n name=\"floorAreaRatio\"\n numeric\n building-complex\n .value=${this.project?.buildingComplex?.floorAreaRatio?.toString() || ''}\n @input=${this._onInputChange}\n suffix-text=\"%\"\n ></md-outlined-text-field>\n </span>\n </div>\n\n <div row>\n <span>착공일정 ~ 준공일정</span>\n <span\n ><input\n type=\"date\"\n name=\"startDate\"\n project\n .value=${this.project.startDate || ''}\n @input=${this._onInputChange}\n max=\"9999-12-31\"\n />\n ~\n <input\n type=\"date\"\n name=\"endDate\"\n project\n .value=${this.project.endDate || ''}\n @input=${this._onInputChange}\n max=\"9999-12-31\"\n />\n </span>\n </div>\n\n <div row>\n <span>유형</span>\n <span\n ><md-filled-select name=\"sectorType\" project @change=${this._onInputChange}>\n <md-select-option ?selected=${!(this.project as any).sectorType} value=\"\">\n <div slot=\"headline\">선택</div>\n </md-select-option>\n <md-select-option ?selected=${(this.project as any).sectorType === 'PUBLIC'} value=\"PUBLIC\">\n <div slot=\"headline\">공공</div>\n </md-select-option>\n <md-select-option ?selected=${(this.project as any).sectorType === 'PRIVATE'} value=\"PRIVATE\">\n <div slot=\"headline\">민간</div>\n </md-select-option>\n </md-filled-select>\n </span>\n </div>\n <div row>\n <span>용도</span>\n <span\n ><md-filled-select name=\"buildingUsage\" project @change=${this._onInputChange}>\n <md-select-option ?selected=${!(this.project as any).buildingUsage} value=\"\">\n <div slot=\"headline\">선택</div>\n </md-select-option>\n <md-select-option\n ?selected=${(this.project as any).buildingUsage === 'RESIDENTIAL'}\n value=\"RESIDENTIAL\"\n >\n <div slot=\"headline\">주거</div>\n </md-select-option>\n <md-select-option\n ?selected=${(this.project as any).buildingUsage === 'NON_RESIDENTIAL'}\n value=\"NON_RESIDENTIAL\"\n >\n <div slot=\"headline\">비주거</div>\n </md-select-option>\n </md-filled-select>\n </span>\n </div>\n\n <div row>\n <span>발주처</span>\n <span\n ><md-outlined-text-field\n type=\"text\"\n name=\"clientCompany\"\n building-complex\n .value=${this.project?.buildingComplex?.clientCompany || ''}\n @input=${this._onInputChange}\n ></md-outlined-text-field>\n </span>\n </div>\n <div row>\n <span>건설사</span>\n <span\n ><md-outlined-text-field\n type=\"text\"\n name=\"constructionCompany\"\n building-complex\n .value=${this.project?.buildingComplex?.constructionCompany || ''}\n @input=${this._onInputChange}\n ></md-outlined-text-field>\n </span>\n </div>\n <div row>\n <span>설계사</span>\n <span\n ><md-outlined-text-field\n type=\"text\"\n name=\"designCompany\"\n building-complex\n .value=${this.project?.buildingComplex?.designCompany || ''}\n @input=${this._onInputChange}\n ></md-outlined-text-field>\n </span>\n </div>\n <div row>\n <span>감리사</span>\n <span\n ><md-outlined-text-field\n type=\"text\"\n name=\"supervisoryCompany\"\n building-complex\n .value=${this.project?.buildingComplex?.supervisoryCompany || ''}\n @input=${this._onInputChange}\n ></md-outlined-text-field>\n </span>\n </div>\n\n <div row>\n <span>대표사진 업로드</span>\n <span>\n <ox-input-image\n name=\"mainPhoto\"\n value=${this.project?.mainPhoto?.fullpath || ''}\n @change=${this.onCreateAttachment.bind(this)}\n ></ox-input-image>\n </span>\n </div>\n <div row>\n <span>공사금액</span>\n <span\n ><md-outlined-text-field\n type=\"text\"\n name=\"constructionCost\"\n numeric\n building-complex\n .value=${this.project?.buildingComplex?.constructionCost?.toString() || ''}\n @input=${this._onInputChange}\n suffix-text=\"억원\"\n ></md-outlined-text-field>\n </span>\n </div>\n <div row>\n <span>투입 인력</span>\n <span\n ><md-outlined-text-field\n type=\"text\"\n name=\"workerCount\"\n numeric\n building-complex\n .value=${this.project?.buildingComplex?.workerCount?.toString() || ''}\n @input=${this._onInputChange}\n suffix-text=\"명\"\n ></md-outlined-text-field>\n </span>\n </div>\n </div>\n\n <div detail-info>\n <div>\n <h3>테넌트 관리</h3>\n <div row>\n <span>관리번호</span>\n <span>\n ${this.project.tenantDomain\n ? html`<a href=${`/project/${this.project.code}/`} target=\"_blank\" title=\"테넌트로 이동\"\n >${this.project.code} <md-icon style=\"font-size:14px;vertical-align:middle\">open_in_new</md-icon></a\n >`\n : this.project.code\n ? html`${this.project.code}`\n : html`<span style=\"color:#999\">미발번 (승격 시 자동 부여)</span>`}\n </span>\n </div>\n <div row>\n <span>현재 상태</span>\n <span>\n ${this.project.tenantDomain\n ? html`<b style=\"color:#42b382\">활성 테넌트</b>`\n : this.project.code\n ? html`<b style=\"color:#aa6633\">강등됨</b> (재승격 시 ${this.project.code} 재사용)`\n : html`<b style=\"color:#999\">미승격</b>`}\n </span>\n </div>\n <div row>\n <span></span>\n <span tenant-actions>\n ${this.project.tenantDomain\n ? html`<md-elevated-button @click=${this._demoteTenant} ?disabled=${!this.project.id}>\n <md-icon slot=\"icon\">link_off</md-icon>테넌트 강등\n </md-elevated-button>`\n : html`<md-elevated-button green @click=${this._promoteTenant} ?disabled=${!this.project.id}>\n <md-icon slot=\"icon\">verified</md-icon>테넌트 승격\n </md-elevated-button>`}\n </span>\n </div>\n </div>\n\n\n </div>\n </div>\n `\n }\n\n async pageInitialized(lifecycle: PageLifecycle) {}\n\n async pageUpdated(changes: any, lifecycle: PageLifecycle) {\n if (!this.active) return\n\n this.projectId = await this._resolveProjectId(lifecycle.resourceId || '')\n await this.initProject(this.projectId)\n }\n\n /**\n * resourceId 를 정상 Project.id (UUID) 로 변환한다.\n * - UUID 형식이면 그대로 사용\n * - 그 외 (code 또는 비어있음) 에는 현재 테넌트 컨텍스트의 프로젝트 조회\n */\n private async _resolveProjectId(resourceId: string): Promise<string> {\n if (resourceId && resourceId.length === 36) return resourceId\n\n const response = await client.query({\n query: gql`\n query CurrentProject {\n currentProject {\n id\n }\n }\n `\n })\n\n return response.data?.currentProject?.id || ''\n }\n\n async initProject(projectId: string = '') {\n const response = await client.query({\n query: gql`\n query Project($id: String!, $filters: [Filter!]) {\n project(id: $id) {\n id\n code\n state\n tenantDomain {\n id\n subdomain\n name\n }\n name\n documentNaming\n sectorType\n buildingUsage\n postalCode\n geoGroup\n startDate\n endDate\n mainPhoto {\n fullpath\n }\n totalProgress\n weeklyProgress\n kpi\n inspPassRate\n robotProgressRate\n structuralSafetyRate\n robotCount\n buildingComplex {\n id\n address\n siteType\n latitude\n longitude\n area\n coverageRatio\n floorAreaRatio\n structureType\n workerCount\n designChangeCount\n plannedProgress\n actualProgress\n clientCompany\n constructionCompany\n supervisoryCompany\n designCompany\n drawing {\n id\n name\n }\n constructionType\n constructionCost\n etc\n notice\n householdCount\n buildingCount\n overallConstructorEmails\n taskConstructorEmails\n overallSupervisoryEmails\n taskSupervisoryEmails\n virtualTourLink\n buildings {\n id\n name\n floorCount\n hasBasement\n basementFloorCount\n }\n }\n }\n\n employees(filters: $filters) {\n items {\n id\n name\n jobResponsibility\n active\n user {\n id\n name\n email\n }\n }\n }\n }\n `,\n variables: {\n id: projectId,\n filters: [\n {\n name: 'active',\n operator: 'eq',\n value: true\n },\n {\n name: 'userId',\n operator: 'is_not_null',\n value: ''\n }\n ]\n },\n context: { headers: tenantHeaders((this.project as any)?.code) }\n })\n\n this.project = response.data?.project || { ...this.defaultProject }\n\n const items = response.data?.employees?.items || []\n this.overallConstructorList = this._filterUserByPermission(items, 'OVERALL_CONSTRUCTOR')\n this.taskConstructorList = this._filterUserByPermission(items, 'TASK_CONSTRUCTOR')\n this.overallSupervisoryList = this._filterUserByPermission(items, 'OVERALL_SUPERVISORY')\n this.taskSupervisoryList = this._filterUserByPermission(items, 'TASK_SUPERVISORY')\n\n this.updateContext()\n }\n\n private _filterUserByPermission(userList, permission: string) {\n return userList\n .filter(v => v.jobResponsibility == permission || v.jobResponsibility == 'ADMIN')\n .map(v => ({ name: v.name, value: v.user.email }))\n }\n\n private _stripTypename(obj: any): any {\n if (Array.isArray(obj)) {\n return obj.map(item => this._stripTypename(item))\n }\n if (obj && typeof obj === 'object' && !(obj instanceof File)) {\n const cleaned: any = {}\n for (const [key, value] of Object.entries(obj)) {\n if (key === '__typename') continue\n cleaned[key] = this._stripTypename(value)\n }\n return cleaned\n }\n return obj\n }\n\n private async _saveProject() {\n // 첨부 파일 필드 제거 (첨부 파일은 {filename}Upload 로 전송)\n delete this.project.mainPhoto\n delete this.project.buildingComplex.drawing\n // ProjectPatch 입력 타입에 없는 표시 전용 필드 제거 (테넌트 승격 UI 표시용)\n const projectCode = (this.project as any).code // 도메인 헤더 용으로 미리 보존\n delete (this.project as any).code\n delete (this.project as any).tenantDomain\n // state 는 raw 값('10'/'20')으로 로드되지만 ProjectPatch 는 enum 키 기대 — 일반\n // save 에서는 state 변경 의도 없으므로 patch 에서 제외. (state 변경은 _onUnfinalize 별도)\n delete (this.project as any).state\n\n // buildingComplex.siteType 가 SiteType enum 외 값 ('BUILDING' 같은 옛 잔재) 이면\n // mutation validation 실패. enum 에 없으면 null 로 정정.\n const validSiteTypes = new Set(Object.values(SiteType))\n if (\n this.project.buildingComplex?.siteType &&\n !validSiteTypes.has(this.project.buildingComplex.siteType as any)\n ) {\n this.project.buildingComplex.siteType = null as any\n }\n\n const response = await client.mutate({\n mutation: gql`\n mutation UpdateProject($project: ProjectPatch!) {\n updateProject(project: $project) {\n id\n }\n }\n `,\n variables: {\n project: this._stripTypename(this.project)\n },\n context: {\n hasUpload: true,\n headers: tenantHeaders(projectCode)\n }\n })\n\n if (!response.errors) {\n // 양방향 동기 — 연면적/용적율/투입인력/설계변경건을 KPI 기준정보(KpiMetricValue)에 반영하고,\n // 공사일정(startDate/endDate)은 KPI \"계획기간\"(constructionPeriod)으로 단방향 반영.\n // 보조 동기 실패가 저장 성공 UX 를 막지 않도록 warn 만.\n try {\n await syncProjectInfoToKpi(this.project, projectCode)\n } catch (e) {\n console.warn('[프로젝트정보→KPI] KpiMetricValue 동기 실패', e)\n }\n notify({ message: '저장에 성공하였습니다.' })\n }\n }\n\n /**\n * 완료(COMPLETED) 상태 프로젝트를 진행중(ONGOING) 으로 되돌림.\n * ProjectPatch 의 state 필드만 갱신 — 다른 폼 필드는 건드리지 않음.\n */\n private _onUnfinalize = async () => {\n if (!this.project?.id) return\n const ok = await OxPrompt.open({\n title: '완료 상태 해제',\n confirmButton: { text: '확인' },\n cancelButton: { text: '취소' }\n })\n if (!ok) return\n\n const response = await client.mutate({\n mutation: gql`\n mutation UpdateProject($project: ProjectPatch!) {\n updateProject(project: $project) {\n id\n state\n }\n }\n `,\n variables: { project: { id: this.project.id, state: 'ONGOING' } },\n context: { headers: tenantHeaders((this.project as any)?.code) }\n })\n\n if (!response.errors) {\n notify({ message: '진행중 상태로 변경되었습니다.' })\n await this.initProject(this.project.id)\n }\n }\n\n /**\n * 주소 옆 \"정보 연동\" 버튼 handler.\n * - 현재 폼의 주소를 backend 로 보냄 → backend 가 EnvVar 갱신 + 세움터 시나리오 호출\n * - 결과 (한글 raw + 숫자) 를 받아 BuildingComplex 폼 필드들에 매핑\n * · siteType (한글) → SiteType enum 변환 (BC.siteType, enum 컬럼)\n * · siteType (한글) → BC.constructionType 그대로 (자유 텍스트 컬럼)\n * · area / floorAreaRatio / coverageRatio / householdCount / buildingCount /\n * structureType → BC 동명 컬럼에 직접 대입\n * · upperFloorCount / lowerFloorCount / bldNm / useAprDay / pmsDay → BC 컬럼\n * 없음, 매핑 skip (참고용 콘솔 로그만)\n */\n private async _collectBasicInfo() {\n const address = (this.project?.buildingComplex?.address || '').trim()\n if (!address) {\n notify({ message: '먼저 프로젝트 주소를 입력해 주세요.' })\n return\n }\n this.collectingBasicInfo = true\n try {\n const response = await client.mutate({\n mutation: gql`\n mutation CollectProjectBasicInfo($projectId: String!, $address: String) {\n collectProjectBasicInfo(projectId: $projectId, address: $address) {\n source\n label\n ok\n message\n data\n }\n }\n `,\n variables: { projectId: this.project.id, address },\n context: { headers: tenantHeaders((this.project as any)?.code) }\n })\n if (response.errors) {\n throw new Error(response.errors.map((e: any) => e.message).join('\\n'))\n }\n const result = response.data?.collectProjectBasicInfo\n if (!result?.ok) {\n notify({ message: result?.message || '업데이트할 정보가 없습니다.' })\n return\n }\n const data = result.data || {}\n this._applyBasicInfoToForm(data)\n this.requestUpdate()\n notify({ message: '외부 시스템에서 기본 정보를 가져왔습니다. 검토 후 [저장]을 눌러주세요.' })\n } catch (err) {\n console.warn('[정보 연동] 실패', err)\n notify({ message: '외부 시스템 연동이 일시적으로 안 됩니다.' })\n } finally {\n this.collectingBasicInfo = false\n }\n }\n\n /**\n * 한글 주용도명 → SiteType enum.\n * - NFC normalize + 공백 제거로 보이지 않는 문자/표기 차이 대응\n * - 매핑 미존재시 null\n */\n private _siteTypeFromKorean(korean: string | null | undefined): string | null {\n if (!korean) return null\n const KEYS: Record<string, string> = {\n 공동주택: 'APARTMENT_COMPLEX',\n 단독주택: 'DETACHED_HOUSE',\n 제1종근린생활시설: 'NEIGHBORHOOD_FACILITY',\n 제2종근린생활시설: 'NEIGHBORHOOD_FACILITY',\n 근린생활시설: 'NEIGHBORHOOD_FACILITY',\n 업무시설: 'OFFICE',\n 판매시설: 'COMMERCIAL',\n 교육연구시설: 'EDUCATION',\n 의료시설: 'MEDICAL',\n 문화및집회시설: 'CULTURAL',\n 종교시설: 'RELIGIOUS',\n 공장: 'INDUSTRIAL',\n 창고시설: 'INDUSTRIAL',\n 운수시설: 'TRANSPORT'\n }\n const key = korean.normalize('NFC').replace(/\\s+/g, '').trim()\n return KEYS[key] || null\n }\n\n /**\n * 시나리오 result data 를 폼에 매핑 — BuildingComplex 와 Project 양쪽.\n * 빈 값(null/undefined/'')은 기존 값 유지 — 자동 수집이 사용자 입력을 무리하게 덮지 않도록.\n */\n private _applyBasicInfoToForm(data: Record<string, any>) {\n const bc = { ...this.project.buildingComplex }\n const setBC = (key: string, value: any) => {\n if (value === null || value === undefined || value === '') return\n ;(bc as any)[key] = value\n }\n setBC('area', this._toNum(data.area))\n setBC('floorAreaRatio', this._toNum(data.floorAreaRatio))\n setBC('coverageRatio', this._toNum(data.coverageRatio))\n setBC('householdCount', this._toNum(data.householdCount))\n setBC('buildingCount', this._toNum(data.buildingCount))\n setBC('structureType', data.structureType)\n // siteType: 한글 → SiteType enum 매핑. 매핑 안 되면 BC.siteType 빈 채로.\n if (data.siteType) {\n const enumVal = this._siteTypeFromKorean(data.siteType)\n if (enumVal) (bc as any).siteType = enumVal\n }\n\n // Project 엔티티 컬럼 — 착공일/준공일.\n // 세움터 useAprDay (사용승인일) = 준공으로 매핑. stcnsDay = 착공.\n const project: any = { ...this.project, buildingComplex: bc }\n if (data.startDate) project.startDate = data.startDate\n if (data.endDate) project.endDate = data.endDate\n this.project = project\n\n // 참고 메타 — BC/Project 직접 컬럼 없음, 콘솔로만 (permitDate=건축허가일, bldNm 등)\n console.log(\n '[정보 연동] meta:',\n JSON.stringify({\n upperFloorCount: data.upperFloorCount,\n lowerFloorCount: data.lowerFloorCount,\n bldNm: data.bldNm,\n permitDate: data.permitDate\n })\n )\n }\n\n private _toNum(v: any): number | null {\n if (v === null || v === undefined || v === '') return null\n const n = Number(v)\n return Number.isFinite(n) ? n : null\n }\n\n private async _promoteTenant() {\n if (!this.project.id) return\n\n if (!confirm('이 프로젝트를 테넌트로 승격하시겠습니까?\\n승격 시 관리번호(YYYY-NNNNN)가 자동 발번되며 변경할 수 없습니다.')) {\n return\n }\n\n const response = await client.mutate({\n mutation: gql`\n mutation PromoteProjectToTenant($projectId: String!) {\n promoteProjectToTenant(projectId: $projectId) {\n id\n code\n }\n }\n `,\n variables: { projectId: this.project.id }\n })\n\n if (!response.errors) {\n notify({ message: `테넌트로 승격되었습니다. 관리번호: ${response.data.promoteProjectToTenant.code}` })\n await this.initProject(this.projectId)\n }\n }\n\n private async _demoteTenant() {\n if (!this.project.id) return\n\n if (!confirm('이 프로젝트의 테넌트를 강등하시겠습니까?\\n관리번호는 보존되며 재승격 시 같은 번호가 재사용됩니다.')) {\n return\n }\n\n const response = await client.mutate({\n mutation: gql`\n mutation DemoteProjectTenant($projectId: String!) {\n demoteProjectTenant(projectId: $projectId)\n }\n `,\n variables: { projectId: this.project.id }\n })\n\n if (!response.errors) {\n notify({ message: '테넌트가 강등되었습니다.' })\n await this.initProject(this.projectId)\n }\n }\n\n // 동 적용 버튼을 누르면 입력한 수 만큼 해당 단지에 동 데이터 생성\n private _setBuilding() {\n const buildingCount: number = this.project?.buildingComplex?.buildingCount || 0\n const buildingInitData = { name: undefined, floorCount: undefined }\n\n // 빌딩 데이터가 없으면 빈 배열 넣어줌\n if (!this.project?.buildingComplex?.buildings?.length) {\n this.project.buildingComplex.buildings = []\n }\n\n if (this.project.buildingComplex.buildings.length >= buildingCount) {\n // 동 수가 더 작게 들어오면 기존 배열을 필요한 크기만큼 잘라내기\n this.project.buildingComplex.buildings = [...this.project.buildingComplex.buildings!.slice(0, buildingCount)]\n } else {\n // 동수가 더 크게 들어오면 기존 배열 + 빈 값을 채움\n const additionalCount = buildingCount - this.project.buildingComplex.buildings.length\n const additionalBuildings = Array.from({ length: additionalCount }, () => ({ ...buildingInitData }))\n this.project.buildingComplex.buildings = [...this.project.buildingComplex.buildings, ...additionalBuildings]\n }\n\n // 리렌더링\n this.project = { ...this.project }\n }\n\n // Input 요소의 값이 변경될 때 호출되는 콜백 함수\n /**\n * 우편번호 입력 핸들러 — postalCode 갱신과 동시에 앞 2자리 prefix 로 geoGroup\n * best-guess 자동 매핑. 매핑 안 되면 기존 값 유지.\n */\n private _onPostalCodeChange(event: InputEvent) {\n const target = event.target as HTMLInputElement\n const value = target.value\n ;(this.project as any).postalCode = value\n const inferred = inferGeoGroupFromPostalCode(value)\n if (inferred) (this.project as any).geoGroup = inferred\n }\n\n private _onInputChange(event: InputEvent, idx: number) {\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 if (target.hasAttribute('project')) {\n this.project[target.name] = inputVal\n\n // documentNaming 필드가 변경되면 실시간으로 미리보기 업데이트\n if (target.name === 'documentNaming') {\n this.requestUpdate()\n }\n } else if (target.hasAttribute('building-complex')) {\n this.project.buildingComplex![target.name] = inputVal\n } else if (target.hasAttribute('building')) {\n this.project.buildingComplex.buildings![idx][target.name] = inputVal\n }\n }\n\n // 이미지 업로드\n async onCreateAttachment(e: CustomEvent) {\n const target = e.target as HTMLInputElement\n const file = (target.name === 'mainPhoto' ? e.detail : e.detail[0]) || null\n\n if (target.name === 'mainPhoto') {\n this.project.mainPhotoUpload = file\n } else {\n this.project.buildingComplex.drawingUpload = file\n }\n }\n\n private _handleSelectionChange(e) {\n const name = e.target.getAttribute('name')\n const selectedValues = e.detail.selectedValues\n\n this.project.buildingComplex[name] = selectedValues\n }\n\n private _toggleBasement(idx: number) {\n const building = this.project.buildingComplex.buildings![idx]\n building.hasBasement = !building.hasBasement\n\n // 층 수가 0이거나 체크 해제시 지하층수 초기화 (플레이스 홀더 나오게 하기 위함)\n if (building.basementFloorCount == 0 || !building.hasBasement) {\n building.basementFloorCount = undefined\n }\n\n this.project.buildingComplex.buildings![idx]['hasBasement'] = building.hasBasement\n this.requestUpdate()\n }\n}\n"]}
|
|
@@ -2,6 +2,12 @@ export declare const getProject: (projectId: string, projectCode?: string) => Pr
|
|
|
2
2
|
export declare const getKpiMetrics: (projectCode?: string) => Promise<any>;
|
|
3
3
|
export declare const getKpiMetricValues: (projectId: string, projectCode?: string) => Promise<any>;
|
|
4
4
|
export declare const getKpiCategories: (projectCode?: string) => Promise<any>;
|
|
5
|
+
/**
|
|
6
|
+
* BuildingComplex 컬럼 부분 업데이트. updateBuildingComplex 는 기존 row 를 load 후
|
|
7
|
+
* patch 를 merge 저장하므로 (building-complex-mutation.ts) 지정 컬럼만 안전하게 갱신
|
|
8
|
+
* — updateProject 처럼 buildings 재생성 side-effect 가 없다.
|
|
9
|
+
*/
|
|
10
|
+
export declare const updateBuildingComplexFields: (id: string, patch: any, projectCode?: string) => Promise<import("@apollo/client").InteropMutateResult<any>>;
|
|
5
11
|
export declare const updateProjectCompleteStep1: (patches: any, projectCode?: string) => Promise<import("@apollo/client").InteropMutateResult<any>>;
|
|
6
12
|
export declare const updateProjectCompleteStep2: (patches: any, projectCode?: string) => Promise<import("@apollo/client").InteropMutateResult<any>>;
|
|
7
13
|
export declare const updateProjectCompleteStep3: (file: any, projectId: string, projectCode?: string) => Promise<import("@apollo/client").InteropMutateResult<any>>;
|