@dssp/dkpi 1.0.0-alpha.80 → 1.0.0-alpha.81
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/bootstrap.d.ts +1 -0
- package/dist-client/bootstrap.js +11 -0
- package/dist-client/bootstrap.js.map +1 -1
- package/dist-client/components/kpi-single-boxplot-chart.d.ts +3 -2
- package/dist-client/components/kpi-single-boxplot-chart.js +30 -23
- package/dist-client/components/kpi-single-boxplot-chart.js.map +1 -1
- package/dist-client/pages/component/project-update-header.d.ts +1 -0
- package/dist-client/pages/component/project-update-header.js +127 -0
- package/dist-client/pages/component/project-update-header.js.map +1 -0
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.d.ts +1 -1
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js +1 -1
- package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js.map +1 -1
- package/dist-client/pages/kpi-value/kpi-value-list-page.js +1 -1
- package/dist-client/pages/kpi-value/kpi-value-list-page.js.map +1 -1
- package/dist-client/pages/project-complete-tabs/pc-tab1-plan.d.ts +16 -2
- package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js +138 -128
- package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js.map +1 -1
- package/dist-client/pages/project-complete-tabs/pc-tab2-rating.d.ts +4 -2
- package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js +109 -44
- package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js.map +1 -1
- package/dist-client/pages/project-complete-tabs/pc-tab3-upload.d.ts +3 -0
- package/dist-client/pages/project-complete-tabs/pc-tab3-upload.js +32 -4
- package/dist-client/pages/project-complete-tabs/pc-tab3-upload.js.map +1 -1
- package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.d.ts +24 -0
- package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.js +365 -157
- package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.js.map +1 -1
- package/dist-client/pages/sv-project-complete.d.ts +4 -1
- package/dist-client/pages/sv-project-complete.js +43 -12
- package/dist-client/pages/sv-project-complete.js.map +1 -1
- package/dist-client/pages/sv-project-detail.d.ts +11 -0
- package/dist-client/pages/sv-project-detail.js +184 -48
- package/dist-client/pages/sv-project-detail.js.map +1 -1
- package/dist-client/pages/sv-project-list.d.ts +9 -0
- package/dist-client/pages/sv-project-list.js +93 -3
- package/dist-client/pages/sv-project-list.js.map +1 -1
- package/dist-client/pages/sv-project-update.d.ts +86 -0
- package/dist-client/pages/sv-project-update.js +1331 -0
- package/dist-client/pages/sv-project-update.js.map +1 -0
- package/dist-client/route.d.ts +1 -1
- package/dist-client/route.js +3 -0
- package/dist-client/route.js.map +1 -1
- package/dist-client/shared/complete-api.d.ts +10 -9
- package/dist-client/shared/complete-api.js +44 -18
- package/dist-client/shared/complete-api.js.map +1 -1
- package/dist-client/tsconfig.tsbuildinfo +1 -1
- package/dist-client/viewparts/menu-tools.js +9 -18
- package/dist-client/viewparts/menu-tools.js.map +1 -1
- package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.d.ts +23 -0
- package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js +72 -28
- package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js.map +1 -1
- package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js +9 -2
- package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js.map +1 -1
- package/dist-server/service/kpi-stat/kpi-stat-query.js +19 -18
- package/dist-server/service/kpi-stat/kpi-stat-query.js.map +1 -1
- package/dist-server/service/kpi-value/kpi-value-query.js +2 -2
- package/dist-server/service/kpi-value/kpi-value-query.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -3
- package/schema.graphql +13 -1
- package/things-factory.config.js +1 -0
- package/dist-client/shared/domain-context.d.ts +0 -7
- package/dist-client/shared/domain-context.js +0 -13
- package/dist-client/shared/domain-context.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pc-tab4-monthly.js","sourceRoot":"","sources":["../../../client/pages/project-complete-tabs/pc-tab4-monthly.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, LitElement } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { client } from '@operato/graphql'\nimport { gql } from '@apollo/client'\nimport { notify } from '@operato/layout'\nimport moment from 'moment-timezone'\n\nconst DATASET_ID = 'fd4092f5-11d0-488a-bbe8-21d2793e1e79'\n\n// 월별 수집 대상 항목 (Dataset의 dataItems tag 기준)\nconst MONTHLY_ITEMS = [\n { tag: 'planned_progress', name: '계획공정율', unit: '%', type: 'number' },\n { tag: 'actual_progress', name: '실적공정율', unit: '%', type: 'number' },\n { tag: 'schedule_deviation', name: '일정 이탈 수준', unit: '', type: 'rating' },\n { tag: 'schedule_assessment', name: '일정성과 수준 평가', unit: '', type: 'rating' },\n { tag: 'cost_assessment', name: '비용성과 수준 평가', unit: '', type: 'rating' },\n { tag: 'quality_assessment', name: '품질성과 수준 평가', unit: '', type: 'rating' },\n { tag: 'safety_assessment', name: '안전성과 수준 평가', unit: '', type: 'rating' },\n { tag: 'environment_assessment', name: '환경성과 수준 평가', unit: '', type: 'rating' },\n { tag: 'productivity_assessment', name: '생산성성과 수준 평가', unit: '', type: 'rating' }\n]\n\n@customElement('sv-pc-tab4-monthly')\nexport class SvProjectCompleteTab4Monthly 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 margin-bottom: 8px;\n }\n\n .toolbar {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 12px;\n margin-bottom: 12px;\n }\n .toolbar select {\n padding: 5px 8px;\n border: 1px solid rgba(0, 0, 0, 0.15);\n border-radius: 5px;\n font-size: 14px;\n color: #212529;\n }\n .toolbar .add-btn {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 5px 10px;\n background: #35618e;\n color: #fff;\n border-radius: 5px;\n cursor: pointer;\n font-size: 13px;\n }\n\n .grid-wrapper {\n overflow-x: auto;\n padding: 0 6px;\n }\n table {\n width: 100%;\n border-collapse: collapse;\n font-size: 13px;\n }\n thead th {\n background: #f3f3fa;\n border-top: 2px solid #0c4da2;\n color: #212529;\n text-align: center;\n padding: 8px 6px;\n white-space: nowrap;\n font-weight: 500;\n }\n tbody td {\n border-bottom: 1px solid rgba(0, 0, 0, 0.08);\n text-align: center;\n padding: 6px 4px;\n vertical-align: middle;\n }\n tbody tr:hover {\n background: #f8fafc;\n }\n .month-cell {\n color: #35618e;\n font-weight: 600;\n white-space: nowrap;\n }\n .month-cell.current {\n color: #16a085;\n }\n\n input[type='number'] {\n width: 70px;\n padding: 4px 6px;\n border: 1px solid rgba(0, 0, 0, 0.12);\n border-radius: 4px;\n text-align: center;\n font-size: 13px;\n }\n input[type='number']:focus {\n outline: none;\n border-color: #35618e;\n }\n\n .rating-select {\n padding: 4px 6px;\n border: 1px solid rgba(0, 0, 0, 0.12);\n border-radius: 4px;\n font-size: 13px;\n text-align: center;\n }\n\n .status-saved {\n color: #16a085;\n font-size: 12px;\n }\n .status-unsaved {\n color: #e67e22;\n font-size: 12px;\n }\n .status-new {\n color: #3498db;\n font-size: 12px;\n }\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\n .delete-btn {\n cursor: pointer;\n color: #999;\n font-size: 18px;\n }\n .delete-btn:hover {\n color: #e74c3c;\n }\n\n .empty-msg {\n text-align: center;\n color: #999;\n padding: 40px 0;\n font-size: 14px;\n }\n `\n ]\n\n @property({ type: Object }) project: any = {}\n\n // monthRows: { workDate: 'YYYY-MM', data: {tag: value}, sampleId?: string, dirty: boolean }[]\n @state() monthRows: any[] = []\n @state() addYear: number = new Date().getFullYear()\n @state() addMonth: number = new Date().getMonth() + 1\n\n render() {\n const years = this._getYearRange()\n\n return html`\n <div class=\"title\">\n 프로젝트 수행 기간 동안의 월별 데이터를 입력합니다.<br />\n 공정률, 감리자 수준 평가(1~5) 등의 항목을 월 단위로 기록하여 성과 추세 분석에 활용합니다.\n </div>\n\n <div class=\"toolbar\">\n <select .value=${String(this.addYear)} @change=${(e: Event) => (this.addYear = Number((e.target as HTMLSelectElement).value))}>\n ${years.map(y => html`<option value=${y} ?selected=${y === this.addYear}>${y}년</option>`)}\n </select>\n <select\n .value=${String(this.addMonth)}\n @change=${(e: Event) => (this.addMonth = Number((e.target as HTMLSelectElement).value))}\n >\n ${[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].map(\n m => html`<option value=${m} ?selected=${m === this.addMonth}>${m}월</option>`\n )}\n </select>\n <div class=\"add-btn\" @click=${this._addMonth}>+ 월 추가</div>\n </div>\n\n ${this.monthRows.length === 0\n ? html`<div class=\"empty-msg\">등록된 월별 데이터가 없습니다. 위에서 월을 추가해주세요.</div>`\n : html`\n <div class=\"grid-wrapper\">\n <table>\n <thead>\n <tr>\n <th>월</th>\n ${MONTHLY_ITEMS.map(item => html`<th>${item.name}${item.unit ? ` (${item.unit})` : ''}</th>`)}\n <th>상태</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n ${this.monthRows.map(\n (row, rowIdx) => html`\n <tr>\n <td class=\"month-cell ${this._isCurrentMonth(row.workDate) ? 'current' : ''}\">${row.workDate}</td>\n ${MONTHLY_ITEMS.map(item =>\n item.type === 'rating'\n ? html`\n <td>\n <select\n class=\"rating-select\"\n .value=${String(row.data[item.tag] ?? '')}\n @change=${(e: Event) => this._onCellChange(rowIdx, item.tag, (e.target as HTMLSelectElement).value)}\n >\n <option value=\"\">-</option>\n ${[1, 2, 3, 4, 5].map(\n v =>\n html`<option value=${v} ?selected=${Number(row.data[item.tag]) === v}>${v}</option>`\n )}\n </select>\n </td>\n `\n : html`\n <td>\n <input\n type=\"number\"\n .value=${row.data[item.tag] ?? ''}\n @input=${(e: InputEvent) => this._onCellChange(rowIdx, item.tag, (e.target as HTMLInputElement).value)}\n />\n </td>\n `\n )}\n <td>\n ${row.sampleId\n ? row.dirty\n ? html`<span class=\"status-unsaved\">수정됨</span>`\n : html`<span class=\"status-saved\">저장됨</span>`\n : html`<span class=\"status-new\">신규</span>`}\n </td>\n <td>\n <span class=\"delete-btn\" title=\"삭제\" @click=${() => this._removeMonth(rowIdx)}>✕</span>\n </td>\n </tr>\n `\n )}\n </tbody>\n </table>\n </div>\n `}\n\n <div class=\"button-line\">\n <div class=\"ghost-btn\" @click=${this._reset}>초기화</div>\n <div class=\"ghost-btn secondary\" @click=${this._save}>저장</div>\n </div>\n `\n }\n\n willUpdate(changedProperties: Map<string, any>) {\n super.willUpdate(changedProperties)\n if (changedProperties.has('project') && this.project?.id) {\n this._loadData()\n }\n }\n\n private _getYearRange(): number[] {\n const startYear = this.project?.startDate ? new Date(this.project.startDate).getFullYear() : new Date().getFullYear() - 2\n const endYear = this.project?.endDate\n ? new Date(this.project.endDate).getFullYear() + 1\n : new Date().getFullYear() + 1\n const years: number[] = []\n for (let y = startYear; y <= endYear; y++) years.push(y)\n return years\n }\n\n private _isCurrentMonth(workDate: string): boolean {\n return workDate === moment().tz('Asia/Seoul').format('YYYY-MM')\n }\n\n private async _loadData() {\n try {\n const response = await client.query({\n query: gql`\n query DataSamplesByDataSet($dataSetId: String!, $filters: [Filter!], $sortings: [Sorting!], $pagination: Pagination) {\n dataSamplesByDataSet(dataSetId: $dataSetId, filters: $filters, sortings: $sortings, pagination: $pagination) {\n items {\n id\n data\n workDate\n key01\n }\n total\n }\n }\n `,\n variables: {\n dataSetId: DATASET_ID,\n filters: [{ name: 'key01', operator: 'eq', value: this.project.id }],\n sortings: [{ name: 'workDate', desc: false }],\n pagination: { page: 1, limit: 120 }\n }\n })\n\n const items = response.data?.dataSamplesByDataSet?.items || []\n this.monthRows = items.map((sample: any) => ({\n workDate: sample.workDate?.substring(0, 7) || '', // YYYY-MM\n data: sample.data || {},\n sampleId: sample.id,\n dirty: false\n }))\n } catch (e) {\n console.error('Failed to load monthly data:', e)\n this.monthRows = []\n }\n }\n\n private _addMonth() {\n const workDate = `${this.addYear}-${String(this.addMonth).padStart(2, '0')}`\n\n // 중복 체크\n if (this.monthRows.some(r => r.workDate === workDate)) {\n notify({ message: `${workDate}은 이미 존재합니다.` })\n return\n }\n\n const newRow = {\n workDate,\n data: {},\n sampleId: null,\n dirty: true\n }\n\n // 정렬된 위치에 삽입\n const rows = [...this.monthRows, newRow]\n rows.sort((a, b) => a.workDate.localeCompare(b.workDate))\n this.monthRows = rows\n }\n\n private _removeMonth(rowIdx: number) {\n this.monthRows = this.monthRows.filter((_, i) => i !== rowIdx)\n }\n\n private _onCellChange(rowIdx: number, tag: string, rawValue: string) {\n const value = rawValue === '' ? null : Number(rawValue)\n this.monthRows = this.monthRows.map((row, i) => {\n if (i !== rowIdx) return row\n return {\n ...row,\n data: { ...row.data, [tag]: value },\n dirty: true\n }\n })\n }\n\n private async _save() {\n const dirtyRows = this.monthRows.filter(r => r.dirty)\n if (dirtyRows.length === 0) {\n notify({ message: '변경된 데이터가 없습니다.' })\n return\n }\n\n let savedCount = 0\n let errorCount = 0\n\n for (const row of dirtyRows) {\n try {\n // data에 project_id 포함 (DataKeySet이 key01에 매핑)\n const data = { ...row.data, project_id: this.project.id }\n // null 값 제거\n for (const key of Object.keys(data)) {\n if (data[key] === null || data[key] === undefined) delete data[key]\n }\n\n // collectedAt을 월 시작일 고정으로 설정 → 동일 key01+collectedAt이면 기존 레코드 업데이트\n const collectedAt = new Date(`${row.workDate}-01T00:00:00Z`)\n\n await client.mutate({\n mutation: gql`\n mutation CreateDataSample($dataSample: NewDataSample!) {\n createDataSample(dataSample: $dataSample) {\n id\n data\n workDate\n key01\n }\n }\n `,\n variables: {\n dataSample: {\n dataSet: { id: DATASET_ID },\n data,\n workDate: `${row.workDate}-01`,\n collectedAt: collectedAt.toISOString(),\n source: 'project-complete'\n }\n }\n })\n\n savedCount++\n } catch (e) {\n console.error(`Failed to save ${row.workDate}:`, e)\n errorCount++\n }\n }\n\n if (errorCount > 0) {\n notify({ message: `${savedCount}건 저장, ${errorCount}건 오류 발생` })\n } else {\n notify({ message: `${savedCount}건 저장되었습니다.` })\n }\n\n // 저장 후 다시 로드하여 sampleId 갱신\n await this._loadData()\n }\n\n private _reset() {\n this._loadData()\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"pc-tab4-monthly.js","sourceRoot":"","sources":["../../../client/pages/project-complete-tabs/pc-tab4-monthly.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, LitElement } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { client } from '@operato/graphql'\nimport { gql } from '@apollo/client'\nimport { notify } from '@operato/layout'\nimport { OxPrompt } from '@operato/popup'\nimport { hasPrivilege } from '@things-factory/auth-base/dist-client'\nimport moment from 'moment-timezone'\nimport { tenantHeaders } from '@dssp/project/dist-client/shared/domain-context'\n\n/**\n * 프로젝트 완공 처리 탭 4 — 월별 KPI 데이터 그리드.\n *\n * 데이터 source = `KpiMetric` (periodType=MONTH) + `KpiMetricValue` (그 metric 의\n * 월별 값들). 이전 버전 (DataSet/DataSample) 폐기. KpiMetricValue 의 unique\n * 인덱스 (domain, metric, valueDate, org) 가 (도메인, 프로젝트, 메트릭, 월) 단일 row\n * 정책을 보장.\n *\n * 단방향 흐름:\n * - 탭1/탭2 의 metric 저장 (valueDate=YYYY-MM-01) → 그 월 row 가 여기 자동 노출\n * - 여기서 cell 수정 → 그 metric × 그 월 row 만 update (탭1/탭2 의 \"현재값\" 영향 없음 —\n * 탭1/탭2 는 KpiMetricValueProvider 가 latest 또는 valueDate-based lookup 으로 동작)\n */\n\n/** rating 셀렉트로 표시할 metric 인지 판단 — 이름에 '평가' 또는 '이탈' 포함되면 1~5 rating. */\nfunction isRating(metricName: string): boolean {\n return /평가|이탈/.test(metricName || '')\n}\n\n@customElement('sv-pc-tab4-monthly')\nexport class SvProjectCompleteTab4Monthly 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 margin-bottom: 8px;\n }\n\n .toolbar {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 12px;\n margin-bottom: 12px;\n }\n .toolbar select {\n padding: 5px 8px;\n border: 1px solid rgba(0, 0, 0, 0.15);\n border-radius: 5px;\n font-size: 14px;\n color: #212529;\n }\n .toolbar .add-btn {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 5px 10px;\n background: #35618e;\n color: #fff;\n border-radius: 5px;\n cursor: pointer;\n font-size: 13px;\n }\n\n .grid-wrapper {\n overflow-x: auto;\n padding: 0 6px;\n }\n table {\n width: 100%;\n border-collapse: collapse;\n font-size: 13px;\n }\n thead th {\n background: #f3f3fa;\n border-top: 2px solid #0c4da2;\n color: #212529;\n text-align: center;\n padding: 8px 6px;\n white-space: nowrap;\n font-weight: 500;\n }\n tbody td {\n border-bottom: 1px solid rgba(0, 0, 0, 0.08);\n text-align: center;\n padding: 6px 4px;\n vertical-align: middle;\n }\n tbody tr:hover {\n background: #f8fafc;\n }\n .month-cell {\n color: #35618e;\n font-weight: 600;\n white-space: nowrap;\n }\n .month-cell.current {\n color: #16a085;\n }\n\n input[type='number'] {\n width: 70px;\n padding: 4px 6px;\n border: 1px solid rgba(0, 0, 0, 0.12);\n border-radius: 4px;\n text-align: center;\n font-size: 13px;\n }\n input[type='number']:focus {\n outline: none;\n border-color: #35618e;\n }\n\n .rating-select {\n padding: 4px 6px;\n border: 1px solid rgba(0, 0, 0, 0.12);\n border-radius: 4px;\n font-size: 13px;\n text-align: center;\n }\n\n .status-saved {\n color: #16a085;\n font-size: 12px;\n }\n .status-unsaved {\n color: #e67e22;\n font-size: 12px;\n }\n .status-new {\n color: #3498db;\n font-size: 12px;\n }\n .status-pending {\n color: #e74c3c;\n font-size: 12px;\n font-weight: 600;\n }\n\n /* 미입력 셀 — 과월/현재월 row 에서 값이 비어있을 때 강조 */\n td.pending input[type='number'],\n td.pending .rating-select {\n border-color: #e74c3c;\n background-color: #fff5f5;\n }\n /* 한 번도 입력된 적 없는 metric 컬럼 — 헤더 강조 */\n th.pending {\n background: #fdecec;\n color: #c0392b;\n }\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 opacity: 0.45;\n cursor: not-allowed;\n }\n\n .delete-btn {\n cursor: pointer;\n color: #999;\n font-size: 18px;\n }\n .delete-btn:hover {\n color: #e74c3c;\n }\n\n .empty-msg {\n text-align: center;\n color: #999;\n padding: 40px 0;\n font-size: 14px;\n }\n `\n ]\n\n @property({ type: Object }) project: any = {}\n\n /** 월별 metric 목록 (periodType=MONTH). KpiMetric admin 에 등록된 것 기준. */\n @state() monthlyMetrics: any[] = []\n\n /** monthRows: { workDate:'YYYY-MM', values:{[metricId]:value}, originalValues, dirty }[] */\n @state() monthRows: any[] = []\n\n @state() addYear: number = new Date().getFullYear()\n @state() addMonth: number = new Date().getMonth() + 1\n /** kpi:input — Step4 월별 데이터 저장 권한 (cumulative 와 같은 권한) */\n @state() canSave = false\n\n async connectedCallback() {\n super.connectedCallback()\n this.canSave = await hasPrivilege({\n category: 'kpi',\n privilege: 'input',\n domainOwnerGranted: true,\n superUserGranted: true\n })\n }\n\n render() {\n const years = this._getYearRange()\n\n return html`\n <div class=\"title\">\n 프로젝트 수행 기간 동안의 월별 데이터를 입력합니다.<br />\n 공정률, 감리자 수준 평가(1~5) 등의 항목을 월 단위로 기록하여 성과 추세 분석에 활용합니다.\n </div>\n\n <div class=\"toolbar\">\n <select\n .value=${String(this.addYear)}\n @change=${(e: Event) => (this.addYear = Number((e.target as HTMLSelectElement).value))}\n >\n ${years.map(y => html`<option value=${y} ?selected=${y === this.addYear}>${y}년</option>`)}\n </select>\n <select\n .value=${String(this.addMonth)}\n @change=${(e: Event) => (this.addMonth = Number((e.target as HTMLSelectElement).value))}\n >\n ${[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12].map(\n m => html`<option value=${m} ?selected=${m === this.addMonth}>${m}월</option>`\n )}\n </select>\n <div class=\"add-btn\" @click=${this._addMonth}>+ 월 추가</div>\n </div>\n\n ${this.monthlyMetrics.length === 0\n ? html`<div class=\"empty-msg\">\n 월별 metric (periodType=MONTH) 이 등록되어 있지 않습니다. KPI 관리자에서 먼저 등록해주세요.\n </div>`\n : this.monthRows.length === 0\n ? html`<div class=\"empty-msg\">등록된 월별 데이터가 없습니다. 위에서 월을 추가해주세요.</div>`\n : html`\n <div class=\"grid-wrapper\">\n <table>\n <thead>\n <tr>\n <th>월</th>\n ${this.monthlyMetrics.map(\n m => html`\n <th class=${this._isMetricNeverEntered(m.id) ? 'pending' : ''}\n title=${this._isMetricNeverEntered(m.id) ? '한 번도 입력된 적 없는 metric' : ''}>\n ${m.name}${m.unit ? ` (${m.unit})` : ''}\n </th>\n `\n )}\n <th>상태</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n ${this.monthRows.map(\n (row, rowIdx) => html`\n <tr>\n <td class=\"month-cell ${this._isCurrentMonth(row.workDate) ? 'current' : ''}\">\n ${row.workDate}\n </td>\n ${this.monthlyMetrics.map(m => {\n const pending = this._isCellPending(row, m.id)\n return isRating(m.name)\n ? html`\n <td class=${pending ? 'pending' : ''}>\n <select\n class=\"rating-select\"\n .value=${String(row.values[m.id] ?? '')}\n @change=${(e: Event) =>\n this._onCellChange(rowIdx, m.id, (e.target as HTMLSelectElement).value)}\n >\n <option value=\"\">-</option>\n ${[1, 2, 3, 4, 5].map(\n v =>\n html`<option value=${v} ?selected=${Number(row.values[m.id]) === v}>\n ${v}\n </option>`\n )}\n </select>\n </td>\n `\n : html`\n <td class=${pending ? 'pending' : ''}>\n <input\n type=\"number\"\n .value=${row.values[m.id] ?? ''}\n @input=${(e: InputEvent) =>\n this._onCellChange(rowIdx, m.id, (e.target as HTMLInputElement).value)}\n />\n </td>\n `\n })}\n <td>\n ${row.dirty\n ? html`<span class=\"status-unsaved\">수정됨</span>`\n : row.isAuto\n ? html`<span class=\"status-pending\">미입력</span>`\n : row.isNew\n ? html`<span class=\"status-new\">신규</span>`\n : html`<span class=\"status-saved\">저장됨</span>`}\n </td>\n <td>\n <span class=\"delete-btn\" title=\"삭제\" @click=${() => this._removeMonth(rowIdx)}>✕</span>\n </td>\n </tr>\n `\n )}\n </tbody>\n </table>\n </div>\n `}\n\n <div class=\"button-line\">\n <div class=\"ghost-btn\" @click=${this._reset}>초기화</div>\n <div\n class=\"ghost-btn secondary ${this.canSave ? '' : 'disabled'}\"\n title=${this.canSave ? '' : 'kpi:input 권한 필요'}\n @click=${() => this.canSave && this._save()}\n >\n 저장\n </div>\n </div>\n `\n }\n\n willUpdate(changedProperties: Map<string, any>) {\n super.willUpdate(changedProperties)\n if (changedProperties.has('project') && this.project?.id) {\n this._loadData()\n }\n }\n\n private _getYearRange(): number[] {\n const startYear = this.project?.startDate\n ? new Date(this.project.startDate).getFullYear()\n : new Date().getFullYear() - 2\n const endYear = this.project?.endDate\n ? new Date(this.project.endDate).getFullYear() + 1\n : new Date().getFullYear() + 1\n const years: number[] = []\n for (let y = startYear; y <= endYear; y++) years.push(y)\n return years\n }\n\n private _isCurrentMonth(workDate: string): boolean {\n return workDate === moment().tz('Asia/Seoul').format('YYYY-MM')\n }\n\n /**\n * 월별 metric 정의 + 그 프로젝트의 월별 KpiMetricValue 들을 조회해 그리드 row 구성.\n *\n * 1) KpiMetric where periodType=MONTH → monthlyMetrics\n * 2) KpiMetricValue where org=projectId → 월별로 그룹핑하여 monthRows\n */\n private async _loadData() {\n try {\n // 1) 월별 metric 목록\n const metricsResp = await client.query({\n query: gql`\n query KpiMetrics {\n kpiMetrics {\n items {\n id\n name\n unit\n periodType\n }\n }\n }\n `,\n context: { headers: tenantHeaders(this.project?.code) }\n })\n const all = metricsResp.data?.kpiMetrics?.items || []\n this.monthlyMetrics = all.filter((m: any) => m.periodType === 'MONTH')\n\n if (this.monthlyMetrics.length === 0) {\n this.monthRows = []\n return\n }\n\n // 2) 프로젝트의 KpiMetricValue 들 (org=projectId). 월별 metric 만 client 측에서 필터.\n const monthlyMetricIds = new Set(this.monthlyMetrics.map((m: any) => m.id))\n const valuesResp = await client.query({\n query: gql`\n query KpiMetricValues($filters: [Filter!]) {\n kpiMetricValues(filters: $filters) {\n items {\n id\n value\n unit\n periodType\n valueDate\n metricId\n }\n }\n }\n `,\n variables: { filters: [{ name: 'org', operator: 'eq', value: this.project.id }] },\n context: { headers: tenantHeaders(this.project?.code) }\n })\n const allValues = valuesResp.data?.kpiMetricValues?.items || []\n // periodType='MONTH' 만 그리드에 — 비월별 row 가 valueDate prefix 로 잘못 묶이지 않도록.\n const monthlyValues = allValues.filter(\n (v: any) => monthlyMetricIds.has(v.metricId) && v.periodType === 'MONTH'\n )\n\n // 월별 그룹핑 — valueDate YYYY-MM-DD 의 앞 7자리. id 도 보존 (삭제 시 사용).\n // 현재월(=this month) row 는 운영 원칙상 표시 대상 아님 → 아예 bucket 제외.\n const currentYm = moment().tz('Asia/Seoul').format('YYYY-MM')\n const byMonth = new Map<string, any>()\n for (const v of monthlyValues) {\n const ym = (v.valueDate || '').slice(0, 7)\n if (!ym) continue\n if (ym === currentYm) continue // 현재월 record 무시 (DB 에 우연히 있더라도)\n if (!byMonth.has(ym))\n byMonth.set(ym, { workDate: ym, values: {}, originalValues: {}, valueIds: {}, dirty: false })\n const row = byMonth.get(ym)\n row.values[v.metricId] = v.value\n row.originalValues[v.metricId] = v.value\n row.valueIds[v.metricId] = v.id\n }\n // project.startDate ~ 현재월 사이 누락된 월 placeholder row 자동 추가.\n // 사용자가 값을 입력하지 않으면 dirty 가 안 되므로 저장 시 무시되어 spurious patch 없음.\n for (const ym of this._generateExpectedMonths()) {\n if (!byMonth.has(ym)) {\n byMonth.set(ym, {\n workDate: ym,\n values: {},\n originalValues: {},\n valueIds: {},\n dirty: false,\n isAuto: true\n })\n }\n }\n\n this.monthRows = Array.from(byMonth.values()).sort((a, b) => b.workDate.localeCompare(a.workDate))\n } catch (e) {\n console.error('Failed to load monthly data:', e)\n this.monthRows = []\n }\n }\n\n /** project.startDate (YYYY-MM) ~ **전월** (YYYY-MM) 까지 매월 문자열 배열.\n * 현재월은 의도적으로 제외 — 운영 원칙상 \"이번달 데이터는 아직 입력 대상이 아님\". */\n private _generateExpectedMonths(): string[] {\n const startStr = (this.project?.startDate || '').slice(0, 7)\n if (!startStr || !/^\\d{4}-\\d{2}$/.test(startStr)) return []\n const tz = 'Asia/Seoul'\n let cur = moment.tz(startStr, 'YYYY-MM', tz)\n const end = moment.tz(moment().tz(tz).subtract(1, 'month').format('YYYY-MM'), 'YYYY-MM', tz)\n if (!cur.isValid() || cur.isAfter(end)) return []\n const months: string[] = []\n while (cur.isSameOrBefore(end)) {\n months.push(cur.format('YYYY-MM'))\n cur = cur.add(1, 'month')\n }\n return months\n }\n\n /** 셀 미입력 여부 — 값 없음 AND 그 월이 **전월** (직전 한 달) 인 경우만 pending.\n * 과월 전체가 아니라 전월 한 달에 한해서만 강조 — 사용자 입력 흐름(이번 달에 지난달 데이터)과 일치. */\n private _isCellPending(row: any, metricId: string): boolean {\n const v = row.values?.[metricId]\n if (v !== undefined && v !== null && v !== '') return false\n const lastMonth = moment().tz('Asia/Seoul').subtract(1, 'month').format('YYYY-MM')\n return row.workDate === lastMonth\n }\n\n /** metric 컬럼이 전 row 통틀어 한 번도 값이 없으면 헤더 강조. */\n private _isMetricNeverEntered(metricId: string): boolean {\n return !this.monthRows.some(r => {\n const v = r.values?.[metricId]\n return v !== undefined && v !== null && v !== ''\n })\n }\n\n private _addMonth() {\n const workDate = `${this.addYear}-${String(this.addMonth).padStart(2, '0')}`\n\n // 현재월 row 는 추가 금지 — 운영 원칙상 이번달 데이터는 입력 대상 아님.\n const currentYm = moment().tz('Asia/Seoul').format('YYYY-MM')\n if (workDate === currentYm) {\n notify({ message: '현재월(이번 달) 데이터는 입력 대상이 아닙니다.' })\n return\n }\n // 미래월도 차단\n if (workDate > currentYm) {\n notify({ message: '미래월은 입력 대상이 아닙니다.' })\n return\n }\n\n if (this.monthRows.some(r => r.workDate === workDate)) {\n notify({ message: `${workDate}은 이미 존재합니다.` })\n return\n }\n\n const newRow = { workDate, values: {}, originalValues: {}, valueIds: {}, dirty: true, isNew: true }\n const rows = [...this.monthRows, newRow]\n rows.sort((a, b) => b.workDate.localeCompare(a.workDate))\n this.monthRows = rows\n }\n\n private async _removeMonth(rowIdx: number) {\n const row = this.monthRows[rowIdx]\n\n // 신규 row (DB 미반영) — 메모리에서만 제거\n if (row.isNew) {\n this.monthRows = this.monthRows.filter((_, i) => i !== rowIdx)\n return\n }\n\n // 저장된 row — 그 월의 모든 KpiMetricValue id 들을 한 번에 삭제.\n // 권한: things-factory 의 deleteKpiMetricValues 는 category=\"kpi\", privilege=\"mutation\"\n // (superUser/도메인 owner 자동 통과, 일반 사용자는 별도 grant 필요).\n const ids = Object.values(row.valueIds || {}).filter(Boolean) as string[]\n if (ids.length === 0) {\n this.monthRows = this.monthRows.filter((_, i) => i !== rowIdx)\n return\n }\n\n const ok = await OxPrompt.open({\n title: `${row.workDate} 의 월별 데이터를 삭제하시겠습니까?`,\n confirmButton: { text: '삭제' },\n cancelButton: { text: '취소' }\n })\n if (!ok) return\n\n try {\n const response = await client.mutate({\n mutation: gql`\n mutation DeleteKpiMetricValues($ids: [String!]!) {\n deleteKpiMetricValues(ids: $ids)\n }\n `,\n variables: { ids },\n context: { headers: tenantHeaders(this.project?.code) }\n })\n if (response.errors) {\n throw new Error(response.errors.map((e: any) => e.message).join('\\n'))\n }\n notify({ message: `${row.workDate} 월별 데이터 ${ids.length}건 삭제되었습니다.` })\n await this._loadData()\n } catch (e) {\n console.error('Failed to delete monthly values:', e)\n notify({ message: '삭제 중 오류가 발생했습니다. 권한을 확인해주세요.' })\n }\n }\n\n private _onCellChange(rowIdx: number, metricId: string, rawValue: string) {\n const value = rawValue === '' ? null : Number(rawValue)\n this.monthRows = this.monthRows.map((row, i) => {\n if (i !== rowIdx) return row\n return {\n ...row,\n values: { ...row.values, [metricId]: value },\n dirty: true\n }\n })\n }\n\n /**\n * 저장 — dirty row 들 안의 변경된 cell 들을 KpiMetricValuePatch 로 모아\n * updateKpiMetricValuesCumulative 한 번 호출 (backend upsert 가 unique 조합 처리).\n */\n private async _save() {\n const patches: any[] = []\n for (const row of this.monthRows) {\n if (!row.dirty) continue\n for (const m of this.monthlyMetrics) {\n const value = row.values[m.id]\n const original = row.originalValues[m.id]\n if (value === undefined || value === null) continue // 빈 값은 skip (기존 유지)\n if (value === original) continue // 변화 없으면 skip\n patches.push({\n metricId: m.id,\n value,\n unit: m.unit || '',\n org: this.project.id,\n periodType: 'MONTH',\n valueDate: `${row.workDate}-01`\n })\n }\n }\n\n if (patches.length === 0) {\n notify({ message: '변경된 데이터가 없습니다.' })\n return\n }\n\n try {\n const response = await client.mutate({\n mutation: gql`\n mutation UpdateKpiMetricValuesCumulative($patches: [KpiMetricValuePatch!]!) {\n updateKpiMetricValuesCumulative(patches: $patches) {\n id\n }\n }\n `,\n variables: { patches },\n context: { headers: tenantHeaders(this.project?.code) }\n })\n if (response.errors) {\n throw new Error(response.errors.map((e: any) => e.message).join('\\n'))\n }\n notify({ message: `${patches.length}건 저장되었습니다.` })\n await this._loadData()\n } catch (e) {\n console.error('Failed to save monthly values:', e)\n notify({ message: '저장 중 오류가 발생했습니다.' })\n }\n }\n\n private _reset() {\n this._loadData()\n }\n}\n"]}
|
|
@@ -13,10 +13,13 @@ export declare class SvProjectCompletePage extends SvProjectCompletePage_base {
|
|
|
13
13
|
private activeTab;
|
|
14
14
|
private projectId;
|
|
15
15
|
private project;
|
|
16
|
+
/** kpi:recalculate — KPI 재계산 권한 */
|
|
17
|
+
private canRecalculate;
|
|
18
|
+
connectedCallback(): Promise<void>;
|
|
16
19
|
render(): import("lit-html").TemplateResult<1>;
|
|
17
20
|
pageUpdated(changes: any, lifecycle: any): Promise<void>;
|
|
18
21
|
private _resolveProjectId;
|
|
19
22
|
private _onTabClick;
|
|
20
|
-
private
|
|
23
|
+
private _onRecalculate;
|
|
21
24
|
}
|
|
22
25
|
export {};
|
|
@@ -8,9 +8,11 @@ import './project-complete-tabs/pc-tab1-plan';
|
|
|
8
8
|
import './project-complete-tabs/pc-tab2-rating';
|
|
9
9
|
import './project-complete-tabs/pc-tab3-upload';
|
|
10
10
|
import './project-complete-tabs/pc-tab4-monthly';
|
|
11
|
-
import { getProject,
|
|
11
|
+
import { getProject, recalculateProjectKpi } from '../shared/complete-api';
|
|
12
|
+
import { isProjectTypeDomain } from '@dssp/project/dist-client/shared/domain-context';
|
|
12
13
|
import { notify } from '@operato/layout';
|
|
13
14
|
import { OxPrompt } from '@operato/popup';
|
|
15
|
+
import { hasPrivilege } from '@things-factory/auth-base/dist-client';
|
|
14
16
|
import { client } from '@operato/graphql';
|
|
15
17
|
import gql from 'graphql-tag';
|
|
16
18
|
let SvProjectCompletePage = class SvProjectCompletePage extends ScopedElementsMixin(PageView) {
|
|
@@ -19,19 +21,25 @@ let SvProjectCompletePage = class SvProjectCompletePage extends ScopedElementsMi
|
|
|
19
21
|
this.activeTab = 1;
|
|
20
22
|
this.projectId = '';
|
|
21
23
|
this.project = {};
|
|
24
|
+
/** kpi:recalculate — KPI 재계산 권한 */
|
|
25
|
+
this.canRecalculate = false;
|
|
22
26
|
// 탭 클릭
|
|
23
27
|
this._onTabClick = async (tabNumber) => {
|
|
24
28
|
if (tabNumber === this.activeTab)
|
|
25
29
|
return;
|
|
26
30
|
this.activeTab = tabNumber;
|
|
27
31
|
};
|
|
28
|
-
//
|
|
29
|
-
this.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
// KPI 재계산 — 입력된 데이터로 산정
|
|
33
|
+
this._onRecalculate = async () => {
|
|
34
|
+
var _a;
|
|
35
|
+
if (await OxPrompt.open({
|
|
36
|
+
title: 'KPI 를 재계산하시겠습니까?',
|
|
37
|
+
confirmButton: { text: '확인' },
|
|
38
|
+
cancelButton: { text: '취소' }
|
|
39
|
+
})) {
|
|
40
|
+
const result = await recalculateProjectKpi(this.projectId, (_a = this.project) === null || _a === void 0 ? void 0 : _a.code);
|
|
32
41
|
if (!result.errors) {
|
|
33
|
-
notify({ message: '
|
|
34
|
-
navigate(`project-detail/${this.projectId}`);
|
|
42
|
+
notify({ message: 'KPI 재계산이 완료되었습니다.', level: 'info' });
|
|
35
43
|
}
|
|
36
44
|
}
|
|
37
45
|
};
|
|
@@ -39,22 +47,37 @@ let SvProjectCompletePage = class SvProjectCompletePage extends ScopedElementsMi
|
|
|
39
47
|
get context() {
|
|
40
48
|
var _a;
|
|
41
49
|
return {
|
|
42
|
-
title:
|
|
50
|
+
title: !isProjectTypeDomain() && this.project
|
|
51
|
+
? `KPI 데이타입력 - ${(_a = this.project) === null || _a === void 0 ? void 0 : _a.name}`
|
|
52
|
+
: 'KPI 데이타입력'
|
|
43
53
|
};
|
|
44
54
|
}
|
|
55
|
+
async connectedCallback() {
|
|
56
|
+
super.connectedCallback();
|
|
57
|
+
this.canRecalculate = await hasPrivilege({
|
|
58
|
+
category: 'kpi',
|
|
59
|
+
privilege: 'recalculate',
|
|
60
|
+
domainOwnerGranted: true,
|
|
61
|
+
superUserGranted: true
|
|
62
|
+
});
|
|
63
|
+
}
|
|
45
64
|
render() {
|
|
46
65
|
return html `
|
|
47
66
|
<div class="page-header">
|
|
48
|
-
<div class="page-title"
|
|
67
|
+
<div class="page-title">KPI 데이타입력</div>
|
|
49
68
|
<div class="triangle"></div>
|
|
50
69
|
<span class="spacer"></span>
|
|
51
70
|
<div class="ghost-btn" @click=${() => navigate(`project-detail/${this.projectId}`)}>
|
|
52
71
|
<md-icon>arrow_back</md-icon>
|
|
53
72
|
<div>상세로 돌아가기</div>
|
|
54
73
|
</div>
|
|
55
|
-
<div
|
|
56
|
-
|
|
57
|
-
|
|
74
|
+
<div
|
|
75
|
+
class="ghost-btn secondary ${this.canRecalculate ? '' : 'disabled'}"
|
|
76
|
+
title=${this.canRecalculate ? '입력된 데이터로 KPI 재계산' : 'kpi:recalculate 권한 필요'}
|
|
77
|
+
@click=${() => this.canRecalculate && this._onRecalculate()}
|
|
78
|
+
>
|
|
79
|
+
<md-icon>refresh</md-icon>
|
|
80
|
+
<div>KPI 재계산</div>
|
|
58
81
|
</div>
|
|
59
82
|
</div>
|
|
60
83
|
|
|
@@ -211,6 +234,10 @@ SvProjectCompletePage.styles = [
|
|
|
211
234
|
.ghost-btn.complete {
|
|
212
235
|
background: #16a085;
|
|
213
236
|
}
|
|
237
|
+
.ghost-btn.disabled {
|
|
238
|
+
opacity: 0.45;
|
|
239
|
+
cursor: not-allowed;
|
|
240
|
+
}
|
|
214
241
|
`
|
|
215
242
|
];
|
|
216
243
|
__decorate([
|
|
@@ -225,6 +252,10 @@ __decorate([
|
|
|
225
252
|
state(),
|
|
226
253
|
__metadata("design:type", Object)
|
|
227
254
|
], SvProjectCompletePage.prototype, "project", void 0);
|
|
255
|
+
__decorate([
|
|
256
|
+
state(),
|
|
257
|
+
__metadata("design:type", Boolean)
|
|
258
|
+
], SvProjectCompletePage.prototype, "canRecalculate", void 0);
|
|
228
259
|
SvProjectCompletePage = __decorate([
|
|
229
260
|
customElement('sv-project-complete')
|
|
230
261
|
], SvProjectCompletePage);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sv-project-complete.js","sourceRoot":"","sources":["../../client/pages/sv-project-complete.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\n\nimport { navigate, PageView } from '@operato/shell'\nimport { css, html } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\nimport { ScopedElementsMixin } from '@open-wc/scoped-elements'\n\nimport './project-complete-tabs/pc-tab1-plan'\nimport './project-complete-tabs/pc-tab2-rating'\nimport './project-complete-tabs/pc-tab3-upload'\nimport './project-complete-tabs/pc-tab4-monthly'\nimport { getProject, getKpiCategories, updateProjectCompleteFinalize } from '../shared/complete-api'\nimport { notify } from '@operato/layout'\nimport { OxPrompt } from '@operato/popup'\nimport { client } from '@operato/graphql'\nimport gql from 'graphql-tag'\n\n@customElement('sv-project-complete')\nexport class SvProjectCompletePage extends ScopedElementsMixin(PageView) {\n static styles = [\n css`\n :host {\n display: flex;\n flex-direction: column;\n overflow-y: auto;\n\n width: 100%;\n height: 100%;\n background-color: var(--md-sys-color-background, #f6f6f6);\n }\n\n .page-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 16px 20px 8px 20px;\n }\n .page-title {\n color: #35618e;\n font-weight: 700;\n font-size: 22px;\n letter-spacing: -0.05em;\n }\n .triangle {\n width: 0;\n height: 0;\n border-left: 6px solid transparent;\n border-right: 6px solid transparent;\n border-top: 8px solid #35618e;\n }\n\n .card {\n background: #ffffff;\n border-radius: 8px;\n padding: 12px;\n box-shadow: 2px 2px 2px 0 rgba(0, 0, 0, 0.08);\n margin: 0 20px 20px 20px;\n }\n\n .tabs {\n display: flex;\n justify-content: center;\n padding: 0 12px 8px 12px;\n border-bottom: 1px dashed rgba(0, 0, 0, 0.15);\n }\n .tab {\n display: inline-flex;\n align-items: center;\n padding: 6px 14px;\n border: 1px solid rgba(0, 0, 0, 0.12);\n color: #35618e;\n background: #f3f6f9;\n cursor: pointer;\n font-size: 14px;\n letter-spacing: -0.02em;\n }\n .tab:first-child {\n border-top-left-radius: 10px;\n border-bottom-left-radius: 10px;\n }\n .tab:last-child {\n border-top-right-radius: 10px;\n border-bottom-right-radius: 10px;\n }\n /* 중간 탭 이중 보더 방지: 좌측 보더 제거 */\n .tab + .tab {\n border-left: 0;\n }\n .tab[active] {\n background: #02a8a2;\n color: #ffffff;\n border-color: rgba(148, 163, 184, 0.5);\n font-weight: 700;\n }\n\n .tab-body {\n padding: 12px 8px 4px 8px;\n }\n\n .button-line {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin: 0 20px 10px 20px;\n }\n .spacer {\n flex: 1;\n }\n .ghost-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 7px 12px;\n background: #35618e;\n color: #ffffff;\n border-radius: 5px;\n box-shadow: 2px 2px 2px 0 rgba(0, 0, 0, 0.1);\n cursor: pointer;\n }\n .ghost-btn.secondary {\n background: #3395f1;\n }\n .ghost-btn.complete {\n background: #16a085;\n }\n `\n ]\n\n get context() {\n return {\n title: this.project ? `프로젝트 완공 처리 - ${this.project?.name}` : '프로젝트 완공 처리'\n }\n }\n\n @state() private activeTab: number = 1\n @state() private projectId: string = ''\n @state() private project: any = {}\n\n render() {\n return html`\n <div class=\"page-header\">\n <div class=\"page-title\">프로젝트 완공 처리</div>\n <div class=\"triangle\"></div>\n <span class=\"spacer\"></span>\n <div class=\"ghost-btn\" @click=${() => navigate(`project-detail/${this.projectId}`)}>\n <md-icon>arrow_back</md-icon>\n <div>상세로 돌아가기</div>\n </div>\n <div class=\"ghost-btn complete\" @click=${this._onComplete}>\n <md-icon>check_circle</md-icon>\n <div>완공 처리</div>\n </div>\n </div>\n\n <div class=\"card\">\n <div class=\"tabs\">\n <div class=\"tab\" ?active=${this.activeTab === 1} @click=${() => this._onTabClick(1)}>\n Step1. 프로젝트 기본정보 현행화\n </div>\n <div class=\"tab\" ?active=${this.activeTab === 2} @click=${() => this._onTabClick(2)}>Step2. 프로젝트 완료 평가</div>\n <div class=\"tab\" ?active=${this.activeTab === 3} @click=${() => this._onTabClick(3)}>Step3. 준공 문서 업로드</div>\n <div class=\"tab\" ?active=${this.activeTab === 4} @click=${() => this._onTabClick(4)}>Step4. 월별 데이터 입력</div>\n </div>\n\n <div class=\"tab-body\">\n ${this.activeTab === 1\n ? html`<sv-pc-tab1-plan .project=${this.project}></sv-pc-tab1-plan>`\n : this.activeTab === 2\n ? html`<sv-pc-tab2-rating .project=${this.project}></sv-pc-tab2-rating>`\n : this.activeTab === 3\n ? html`<sv-pc-tab3-upload .project=${this.project}></sv-pc-tab3-upload>`\n : html`<sv-pc-tab4-monthly .project=${this.project}></sv-pc-tab4-monthly>`}\n </div>\n </div>\n `\n }\n\n async pageUpdated(changes: any, lifecycle: any) {\n if (this.active) {\n this.projectId = await this._resolveProjectId(lifecycle.resourceId || '')\n\n if (this.projectId) {\n this.project = await getProject(this.projectId)\n }\n }\n\n this.updateContext()\n }\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 // 탭 클릭\n private _onTabClick = async (tabNumber: number) => {\n if (tabNumber === this.activeTab) return\n this.activeTab = tabNumber\n }\n\n // 완공 처리\n private _onComplete = async () => {\n if (\n await OxPrompt.open({ title: '완공 처리를 하시겠습니까?', confirmButton: { text: '확인' }, cancelButton: { text: '취소' } })\n ) {\n const result = await updateProjectCompleteFinalize(this.projectId)\n if (!result.errors) {\n notify({ message: '완공 처리가 완료되었습니다.', level: 'info' })\n navigate(`project-detail/${this.projectId}`)\n }\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"sv-project-complete.js","sourceRoot":"","sources":["../../client/pages/sv-project-complete.ts"],"names":[],"mappings":"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","sourcesContent":["import '@material/web/icon/icon.js'\n\nimport { navigate, PageView } from '@operato/shell'\nimport { css, html } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\nimport { ScopedElementsMixin } from '@open-wc/scoped-elements'\n\nimport './project-complete-tabs/pc-tab1-plan'\nimport './project-complete-tabs/pc-tab2-rating'\nimport './project-complete-tabs/pc-tab3-upload'\nimport './project-complete-tabs/pc-tab4-monthly'\nimport { getProject, recalculateProjectKpi } from '../shared/complete-api'\nimport { isProjectTypeDomain } from '@dssp/project/dist-client/shared/domain-context'\nimport { notify } from '@operato/layout'\nimport { OxPrompt } from '@operato/popup'\nimport { hasPrivilege } from '@things-factory/auth-base/dist-client'\nimport { client } from '@operato/graphql'\nimport gql from 'graphql-tag'\n\n@customElement('sv-project-complete')\nexport class SvProjectCompletePage extends ScopedElementsMixin(PageView) {\n static styles = [\n css`\n :host {\n display: flex;\n flex-direction: column;\n overflow-y: auto;\n\n width: 100%;\n height: 100%;\n background-color: var(--md-sys-color-background, #f6f6f6);\n }\n\n .page-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 16px 20px 8px 20px;\n }\n .page-title {\n color: #35618e;\n font-weight: 700;\n font-size: 22px;\n letter-spacing: -0.05em;\n }\n .triangle {\n width: 0;\n height: 0;\n border-left: 6px solid transparent;\n border-right: 6px solid transparent;\n border-top: 8px solid #35618e;\n }\n\n .card {\n background: #ffffff;\n border-radius: 8px;\n padding: 12px;\n box-shadow: 2px 2px 2px 0 rgba(0, 0, 0, 0.08);\n margin: 0 20px 20px 20px;\n }\n\n .tabs {\n display: flex;\n justify-content: center;\n padding: 0 12px 8px 12px;\n border-bottom: 1px dashed rgba(0, 0, 0, 0.15);\n }\n .tab {\n display: inline-flex;\n align-items: center;\n padding: 6px 14px;\n border: 1px solid rgba(0, 0, 0, 0.12);\n color: #35618e;\n background: #f3f6f9;\n cursor: pointer;\n font-size: 14px;\n letter-spacing: -0.02em;\n }\n .tab:first-child {\n border-top-left-radius: 10px;\n border-bottom-left-radius: 10px;\n }\n .tab:last-child {\n border-top-right-radius: 10px;\n border-bottom-right-radius: 10px;\n }\n /* 중간 탭 이중 보더 방지: 좌측 보더 제거 */\n .tab + .tab {\n border-left: 0;\n }\n .tab[active] {\n background: #02a8a2;\n color: #ffffff;\n border-color: rgba(148, 163, 184, 0.5);\n font-weight: 700;\n }\n\n .tab-body {\n padding: 12px 8px 4px 8px;\n }\n\n .button-line {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin: 0 20px 10px 20px;\n }\n .spacer {\n flex: 1;\n }\n .ghost-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 7px 12px;\n background: #35618e;\n color: #ffffff;\n border-radius: 5px;\n box-shadow: 2px 2px 2px 0 rgba(0, 0, 0, 0.1);\n cursor: pointer;\n }\n .ghost-btn.secondary {\n background: #3395f1;\n }\n .ghost-btn.complete {\n background: #16a085;\n }\n .ghost-btn.disabled {\n opacity: 0.45;\n cursor: not-allowed;\n }\n `\n ]\n\n get context() {\n return {\n title:\n !isProjectTypeDomain() && this.project\n ? `KPI 데이타입력 - ${this.project?.name}`\n : 'KPI 데이타입력'\n }\n }\n\n @state() private activeTab: number = 1\n @state() private projectId: string = ''\n @state() private project: any = {}\n /** kpi:recalculate — KPI 재계산 권한 */\n @state() private canRecalculate: boolean = false\n\n async connectedCallback() {\n super.connectedCallback()\n this.canRecalculate = await hasPrivilege({\n category: 'kpi',\n privilege: 'recalculate',\n domainOwnerGranted: true,\n superUserGranted: true\n })\n }\n\n render() {\n return html`\n <div class=\"page-header\">\n <div class=\"page-title\">KPI 데이타입력</div>\n <div class=\"triangle\"></div>\n <span class=\"spacer\"></span>\n <div class=\"ghost-btn\" @click=${() => navigate(`project-detail/${this.projectId}`)}>\n <md-icon>arrow_back</md-icon>\n <div>상세로 돌아가기</div>\n </div>\n <div\n class=\"ghost-btn secondary ${this.canRecalculate ? '' : 'disabled'}\"\n title=${this.canRecalculate ? '입력된 데이터로 KPI 재계산' : 'kpi:recalculate 권한 필요'}\n @click=${() => this.canRecalculate && this._onRecalculate()}\n >\n <md-icon>refresh</md-icon>\n <div>KPI 재계산</div>\n </div>\n </div>\n\n <div class=\"card\">\n <div class=\"tabs\">\n <div class=\"tab\" ?active=${this.activeTab === 1} @click=${() => this._onTabClick(1)}>\n Step1. 프로젝트 기본정보 현행화\n </div>\n <div class=\"tab\" ?active=${this.activeTab === 2} @click=${() => this._onTabClick(2)}>Step2. 프로젝트 완료 평가</div>\n <div class=\"tab\" ?active=${this.activeTab === 3} @click=${() => this._onTabClick(3)}>Step3. 준공 문서 업로드</div>\n <div class=\"tab\" ?active=${this.activeTab === 4} @click=${() => this._onTabClick(4)}>Step4. 월별 데이터 입력</div>\n </div>\n\n <div class=\"tab-body\">\n ${this.activeTab === 1\n ? html`<sv-pc-tab1-plan .project=${this.project}></sv-pc-tab1-plan>`\n : this.activeTab === 2\n ? html`<sv-pc-tab2-rating .project=${this.project}></sv-pc-tab2-rating>`\n : this.activeTab === 3\n ? html`<sv-pc-tab3-upload .project=${this.project}></sv-pc-tab3-upload>`\n : html`<sv-pc-tab4-monthly .project=${this.project}></sv-pc-tab4-monthly>`}\n </div>\n </div>\n `\n }\n\n async pageUpdated(changes: any, lifecycle: any) {\n if (this.active) {\n this.projectId = await this._resolveProjectId(lifecycle.resourceId || '')\n\n if (this.projectId) {\n this.project = await getProject(this.projectId)\n }\n }\n\n this.updateContext()\n }\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 // 탭 클릭\n private _onTabClick = async (tabNumber: number) => {\n if (tabNumber === this.activeTab) return\n this.activeTab = tabNumber\n }\n\n // KPI 재계산 — 입력된 데이터로 산정\n private _onRecalculate = async () => {\n if (\n await OxPrompt.open({\n title: 'KPI 를 재계산하시겠습니까?',\n confirmButton: { text: '확인' },\n cancelButton: { text: '취소' }\n })\n ) {\n const result = await recalculateProjectKpi(this.projectId, this.project?.code)\n if (!result.errors) {\n notify({ message: 'KPI 재계산이 완료되었습니다.', level: 'info' })\n }\n }\n }\n}\n"]}
|
|
@@ -16,10 +16,21 @@ export declare class SvProjectDetailPage extends SvProjectDetailPage_base {
|
|
|
16
16
|
private projectXKpiValues;
|
|
17
17
|
private showScoreDistribution;
|
|
18
18
|
private selectedKpi;
|
|
19
|
+
/** 이 프로젝트에 아직 한 번도 입력되지 않은 metric (active 한 manual/import 한정) */
|
|
20
|
+
private pendingMetrics;
|
|
19
21
|
render(): import("lit-html").TemplateResult<1>;
|
|
20
22
|
pageUpdated(changes: any, lifecycle: PageLifecycle): Promise<void>;
|
|
23
|
+
private _resolveProjectCode;
|
|
21
24
|
private _resolveProjectIdFromDomain;
|
|
22
25
|
initProject(projectId?: string): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* 이 프로젝트의 미입력 KPI metric 들을 조회 — Step 1/2 와 동일 기준.
|
|
28
|
+
* - 대상: active=true 인 manual/import metric (자동수집/외부API 제외)
|
|
29
|
+
* - 판정:
|
|
30
|
+
* · MONTH metric → 전월 row (periodType='MONTH', valueDate startsWith 전월) 없으면 pending
|
|
31
|
+
* · 그 외 → (metric, periodType) 매칭 row 없으면 pending
|
|
32
|
+
*/
|
|
33
|
+
private _loadPendingMetrics;
|
|
23
34
|
getKpiComprehensiveStats(): Promise<void>;
|
|
24
35
|
getKpiYComprehensiveStats(): Promise<void>;
|
|
25
36
|
getProjectXKpiValues(projectId: string): Promise<void>;
|