-
${group.kpis.map(
(k, i) => html` `
)}
import { html, css, LitElement } from 'lit' import { customElement, state } from 'lit/decorators.js' import { marked } from 'marked' import { PageView, store } from '@operato/shell' import { i18next, localize } from '@operato/i18n' @customElement('kpi-overview') export class KpiOverview extends PageView { static styles = css` .overview-container { display: flex; gap: 32px; background: #f7f5fa; border-radius: 20px; padding: 32px; margin-bottom: 24px; align-items: stretch; } .overview-left { flex: 1; display: flex; flex-direction: column; justify-content: center; } .overview-title { font-size: 2.8rem; font-weight: bold; margin-bottom: 24px; } .overview-desc { font-size: 1.2rem; color: #222; line-height: 1.6; } .overview-right { flex: 1; display: flex; align-items: center; justify-content: center; } .overview-img { width: 100%; max-width: 420px; border-radius: 12px; object-fit: cover; box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08); } .group-tabs { display: flex; gap: 8px; margin: 0 0 16px 0; justify-content: center; } .group-tab { padding: 10px 28px; border-radius: 16px 16px 0 0; background: #eee; cursor: pointer; font-size: 1.1rem; font-weight: 500; border: none; outline: none; transition: background 0.2s; } .group-tab[selected] { background: #d6d6f7; color: #222; font-weight: bold; } .kpi-list { display: flex; gap: 8px; margin: 0 0 32px 0; justify-content: center; } .kpi-item { padding: 8px 20px; border-radius: 12px; background: #f5f5f5; cursor: pointer; font-size: 1rem; border: none; outline: none; transition: background 0.2s; } .kpi-item[selected] { background: #bdf; color: #222; font-weight: bold; } .main-content { display: flex; gap: 32px; margin-top: 32px; align-items: flex-start; justify-content: center; } .markdown { background: #fff; border-radius: 12px; padding: 40px 32px; min-height: 240px; flex: 2; font-size: 2rem; color: #222; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); } .toc { flex: 1; background: #faf9fd; border-radius: 12px; padding: 32px 24px; font-size: 1.1rem; color: #444; min-width: 220px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03); } .toc-title { font-weight: bold; margin-bottom: 12px; font-size: 1.2rem; } .toc-list { margin: 0 0 16px 0; padding: 0; list-style: none; } .toc-list li { margin-bottom: 6px; } .toc-btn { display: block; margin: 12px 0; padding: 8px 16px; border: 1px solid #aaa; border-radius: 8px; background: #fff; color: #333; cursor: pointer; font-size: 1rem; text-align: left; transition: background 0.2s; } .toc-btn:hover { background: #f0f0f0; } ` get context() { return { title: i18next.t('title.kpi overview') } } @state() selectedGroup = 0 @state() selectedKpi = 0 kpiGroups = [ { name: '일정성과 지표', kpis: ['연면적 대비 공사기간', '설계변경에 따른 공기 증감률', '일정 이탈 수준', '일정성과 수준 평가'] }, { name: '비용성과 지표', kpis: ['연면적 대비 공사비', '설계변경에 따른 공사비 증감률', '비용성과 수준 평가'] }, { name: '품질성과 지표', kpis: ['품질시험 불합격률', '검수재 불합격률', '검측 불합격률', '품질 SL-PA 결과값', '품질성과 수준 평가'] }, { name: '안전성과 지표', kpis: ['재해율', '재해강도율', '안전 SL-PA 결과값', '안전성과 수준 평가'] }, { name: '환경성과 지표', kpis: ['건설폐기물 발생량', '환경성과 수준 평가'] }, { name: '생산성과 지표', kpis: ['투입인력 생산성', '생산성과 수준 평가'] } ] kpiDescriptions = { '연면적 대비 공사기간': `# 연면적 대비 공사기간\n- 설명: 프로젝트의 전체 연면적 대비 실제 소요된 공사기간을 측정합니다.\n- 산식: 실제공사기간(개월) / (연면적/1000m2)\n- 데이터 표준형: 실제공사기간(개월) / (연면적/1000m2)\n- 활용: 대형 프로젝트의 일정 효율성 파악`, '설계변경에 따른 공기 증감률': `# 설계변경에 따른 공기 증감률\n- 설명: 설계변경으로 인한 공기 단축 또는 증가 효과를 측정합니다.\n- 산식: (실제공사기간 - 계획공사기간) / 계획공사기간(개월)\n- 데이터 표준형: (실제공사기간 - 계획공사기간)(개월) / 계획공사기간(개월)`, '일정 이탈 수준': `# 일정 이탈 수준\n- 설명: 계획 대비 실제 일정의 이탈 정도를 평가합니다.\n- 산식: Table(DART 등) 활용\n- 평가 방식: 1~5점 척도(감리자 평가)`, '일정성과 수준 평가': `# 일정성과 수준 평가\n- 설명: 일정성과의 전반적인 수준을 감리자가 평가합니다.\n- 평가 방식: 감리자 1~5점 척도`, '연면적 대비 공사비': `# 연면적 대비 공사비\n- 설명: 프로젝트의 전체 연면적 대비 실제 소요된 공사비를 측정합니다.\n- 산식: 실제공사비(억원) / (연면적/1000m2)\n- 데이터 표준형: 실제공사비(억원) / (연면적/1000m2)`, '설계변경에 따른 공사비 증감률': `# 설계변경에 따른 공사비 증감률\n- 설명: 설계변경으로 인한 공사비 증감률을 측정합니다.\n- 산식: (실제공사비 - 계획공사비) / 계획공사비(억원)\n- 데이터 표준형: (실제공사비 - 계획공사비)(억원) / 계획공사비(억원)`, '비용성과 수준 평가': `# 비용성과 수준 평가\n- 설명: 비용성과의 전반적인 수준을 감리자가 평가합니다.\n- 평가 방식: 감리자 1~5점 척도`, '품질시험 불합격률': `# 품질시험 불합격률\n- 설명: 전체 품질시험 중 불합격 건수의 비율을 측정합니다.\n- 산식: 품질시험 불합격 건수(건) / (연면적/1000m2)`, '검수재 불합격률': `# 검수재 불합격률\n- 설명: 검수재 중 불합격 건수의 비율을 측정합니다.\n- 산식: 검수재 불합격 건수(건) / (연면적/1000m2)`, '검측 불합격률': `# 검측 불합격률\n- 설명: 전체 검측 중 불합격 건수의 비율을 측정합니다.\n- 산식: 품질검측 불합격 건수(건) / 품질검측 건수(건)`, '품질 SL-PA 결과값': `# 품질 SL-PA 결과값\n- 설명: SL-PA(Safety Level - Performance Assessment)는 건설/안전 분야에서 안전관리 수준을 정량적으로 평가하는 대표 지표입니다.\n- 평가 방식: Python 활용 1~5점 척도`, '품질성과 수준 평가': `# 품질성과 수준 평가\n- 설명: 품질성과의 전반적인 수준을 감리자가 평가합니다.\n- 평가 방식: 감리자 1~5점 척도`, 재해율: `# 재해율\n- 설명: 연간 근로자 수 대비 재해 발생 건수의 비율을 측정합니다.\n- 산식: (재해 건수(건) / 연간 근로자 수(명)) * 100`, 재해강도율: `# 재해강도율\n- 설명: 총 근로자 수와 연간 근로시간 대비 노동손실일수의 비율을 측정합니다.\n- 산식: (노동손실일수(일) / (총 근로자 수 * 1인당 연간 근로시간)) * 100`, '안전 SL-PA 결과값': `# 안전 SL-PA 결과값\n- 설명: SL-PA(Safety Level - Performance Assessment)는 건설/안전 분야에서 **안전관리 수준**을 정량적으로 평가하는 대표 지표입니다.\n- 활용: 현장의 안전성과를 수치로 진단하고, 위험요소 개선 및 안전관리 정책 수립에 활용됩니다.\n- 평가 방식: Python 활용 1~5점 척도 (1점: 매우 미흡, 5점: 매우 우수)`, '안전성과 수준 평가': `# 안전성과 수준 평가\n- 설명: 안전성과의 전반적인 수준을 감리자가 평가합니다.\n- 평가 방식: 감리자 1~5점 척도`, '건설폐기물 발생량': `# 건설폐기물 발생량\n- 설명: 전체 건설폐기물 발생량을 측정합니다.\n- 산식: 총 건설 폐기물 발생량(ton) / (연면적/1000m2)`, '환경성과 수준 평가': `# 환경성과 수준 평가\n- 설명: 환경성과의 전반적인 수준을 감리자가 평가합니다.\n- 평가 방식: 감리자 1~5점 척도`, '투입인력 생산성': `# 투입인력 생산성\n- 설명: 투입 인력 대비 생산성을 측정합니다.\n- 산식: 투입인력(명) / (연면적/1000m2)`, '생산성과 수준 평가': `# 생산성과 수준 평가\n- 설명: 생산성과의 전반적인 수준을 감리자가 평가합니다.\n- 평가 방식: 감리자 1~5점 척도` } render() { const group = this.kpiGroups[this.selectedGroup] const kpi = group.kpis[this.selectedKpi] const md = this.kpiDescriptions[kpi] || '각 지표의 주요 소개 내용을 마크다운으로 입력하세요.' return html`