@things-factory/kpi 9.0.41 → 9.0.42

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.
@@ -1,7 +1,6 @@
1
1
  import { __decorate, __metadata } from "tslib";
2
2
  import { html, css } from 'lit';
3
3
  import { customElement, state } from 'lit/decorators.js';
4
- import { marked } from 'marked';
5
4
  import { PageView } from '@operato/shell';
6
5
  import { i18next } from '@operato/i18n';
7
6
  import { client } from '@operato/graphql';
@@ -34,65 +33,68 @@ let KpiOverview = class KpiOverview extends PageView {
34
33
  this.error = null;
35
34
  }
36
35
  static { this.styles = css `
36
+ :host {
37
+ display: block;
38
+ background: #f6f6f6;
39
+ box-sizing: border-box;
40
+ padding: 16px 24px;
41
+ }
37
42
  .overview-container {
38
- display: flex;
39
- gap: 32px;
40
- background: #f7f5fa;
41
- border-radius: 20px;
42
- padding: 32px;
43
- margin-bottom: 24px;
44
- align-items: stretch;
43
+ display: block;
44
+ margin: 8px 0 24px 0;
45
45
  }
46
46
  .overview-left {
47
- flex: 1;
48
- display: flex;
49
- flex-direction: column;
50
- justify-content: center;
47
+ display: block;
51
48
  }
52
49
  .overview-title {
53
- font-size: 2.8rem;
54
- font-weight: bold;
55
- margin-bottom: 24px;
50
+ color: #0c4da2;
51
+ font-size: 1.125rem;
52
+ font-weight: 700;
53
+ letter-spacing: -0.05em;
54
+ margin: 0 0 8px 0;
56
55
  }
57
56
  .overview-desc {
58
- font-size: 1.2rem;
59
- color: #222;
60
- line-height: 1.6;
61
- }
62
- .overview-right {
63
- flex: 1;
64
- display: flex;
65
- align-items: center;
66
- justify-content: center;
67
- }
68
- .overview-img {
69
- width: 100%;
70
- max-width: 420px;
71
- border-radius: 12px;
72
- object-fit: cover;
73
- box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
57
+ font-size: 0.875rem;
58
+ color: #212529;
59
+ letter-spacing: -0.05em;
60
+ line-height: 1;
74
61
  }
75
62
  .group-tabs {
76
63
  display: flex;
77
- gap: 8px;
78
- margin: 0 0 16px 0;
79
- justify-content: center;
64
+ margin: 8px 0 0 0;
65
+ justify-content: flex-start;
80
66
  }
81
67
  .group-tab {
82
- padding: 10px 28px;
83
- border-radius: 16px 16px 0 0;
84
- background: #eee;
68
+ padding: 10px 15px;
69
+ border-radius: 8px 8px 0 0;
70
+ background: rgba(46, 164, 223, 0.1);
85
71
  cursor: pointer;
86
- font-size: 1.1rem;
87
- font-weight: 500;
72
+ font-size: 1rem;
73
+ font-weight: 400;
88
74
  border: none;
89
75
  outline: none;
90
76
  transition: background 0.2s;
91
77
  }
92
78
  .group-tab[selected] {
93
- background: #d6d6f7;
94
- color: #222;
95
- font-weight: bold;
79
+ background: #0c4da2;
80
+ color: #ffffff;
81
+ font-weight: 700;
82
+ box-shadow: 1px 0 0 0 rgba(0, 0, 0, 0.1);
83
+ }
84
+ .tab-icon {
85
+ display: inline-block;
86
+ width: 20px;
87
+ height: 20px;
88
+ border-radius: 2px;
89
+ background: #35618e;
90
+ vertical-align: middle;
91
+ }
92
+ .group-tab[selected] .tab-icon {
93
+ background: #ffffff;
94
+ }
95
+ .tab-label {
96
+ margin-left: 6px;
97
+ vertical-align: middle;
96
98
  }
97
99
  .kpi-list {
98
100
  display: flex;
@@ -117,59 +119,133 @@ let KpiOverview = class KpiOverview extends PageView {
117
119
  }
118
120
  .main-content {
119
121
  display: flex;
120
- gap: 32px;
121
- margin-top: 32px;
122
- align-items: flex-start;
123
- justify-content: center;
122
+ gap: 0;
123
+ align-items: stretch;
124
+ justify-content: stretch;
125
+ background: #fff;
126
+ border: 2px solid #0c4da2;
127
+ overflow: hidden;
124
128
  }
125
129
  .markdown {
126
130
  background: #fff;
127
- border-radius: 12px;
128
- padding: 40px 32px;
131
+ padding: 25px;
129
132
  min-height: 240px;
130
- flex: 2;
131
- font-size: 2rem;
132
- color: #222;
133
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
133
+ flex: 1 1 auto;
134
+ font-size: 0.875rem;
135
+ color: #212529;
136
+ }
137
+ .markdown p {
138
+ line-height: 1.7142857142857142;
139
+ margin: 0 0 12px 0;
134
140
  }
135
141
  .toc {
136
- flex: 1;
137
- background: #faf9fd;
138
- border-radius: 12px;
139
- padding: 32px 24px;
140
- font-size: 1.1rem;
141
- color: #444;
142
- min-width: 220px;
143
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03);
142
+ flex: 0 0 240px;
143
+ background: linear-gradient(180deg, rgba(137, 204, 237, 0.2) 0%, rgba(137, 204, 237, 0) 100%);
144
+ border-left: 1px solid rgba(46, 164, 223, 0.1);
145
+ padding: 30px 15px;
146
+ font-size: 1rem;
147
+ color: #212529;
148
+ }
149
+ .toc img {
150
+ width: 25px;
151
+ height: 25px;
152
+ }
153
+ .submenu-header {
154
+ display: flex;
155
+ align-items: center;
156
+ gap: 6px;
157
+ margin-bottom: 14px;
158
+ }
159
+ .submenu-icon {
160
+ width: 25.5px;
161
+ height: 28.67px;
162
+ border-radius: 2px;
163
+ background: #02a8a2;
164
+ flex: 0 0 auto;
165
+ }
166
+ .submenu-title {
167
+ font-weight: 700;
168
+ font-size: 1.25rem;
169
+ color: #0c4da2;
144
170
  }
145
171
  .toc-title {
146
- font-weight: bold;
147
- margin-bottom: 12px;
148
- font-size: 1.2rem;
172
+ color: #0c4da2;
173
+ font-size: 20px;
174
+ font-weight: 700;
175
+ line-height: 20px;
176
+ margin-bottom: 15px;
177
+ margin-top: 3px;
149
178
  }
150
179
  .toc-list {
151
- margin: 0 0 16px 0;
180
+ margin: 0 0 8px 0;
152
181
  padding: 0;
153
182
  list-style: none;
154
183
  }
155
184
  .toc-list li {
156
- margin-bottom: 6px;
185
+ margin-bottom: 10px;
157
186
  }
158
187
  .toc-btn {
159
188
  display: block;
160
- margin: 12px 0;
161
- padding: 8px 16px;
162
- border: 1px solid #aaa;
163
- border-radius: 8px;
164
- background: #fff;
165
- color: #333;
189
+ margin: 0;
190
+ padding: 8px 0 8px 12px;
191
+ border: none;
192
+ border-left: 3px solid transparent;
193
+ border-radius: 0;
194
+ background: transparent;
195
+ color: #212529;
166
196
  cursor: pointer;
167
197
  font-size: 1rem;
168
198
  text-align: left;
169
199
  transition: background 0.2s;
170
200
  }
171
201
  .toc-btn:hover {
172
- background: #f0f0f0;
202
+ background: rgba(0, 0, 0, 0.03);
203
+ }
204
+ .toc-btn[selected] {
205
+ border-left-color: #4cbb49;
206
+ font-weight: 700;
207
+ color: #212529;
208
+ }
209
+ .kpi-title {
210
+ font-size: 1.125rem;
211
+ font-weight: 700;
212
+ margin-bottom: 8px;
213
+ color: #02a8a2;
214
+ }
215
+ .kpi-formula {
216
+ margin: 3px 0 20px 0;
217
+ font-weight: 700;
218
+ color: #212529;
219
+ }
220
+ .subtitle {
221
+ display: inline-flex;
222
+ align-items: center;
223
+ gap: 6px;
224
+ margin-top: 20px;
225
+ font-weight: 700;
226
+ color: #0c4da2;
227
+ }
228
+ .subtitle img {
229
+ width: 15px;
230
+ height: 15px;
231
+ }
232
+ .subtitle-icon {
233
+ width: 20px;
234
+ height: 20px;
235
+ border-radius: 2px;
236
+ background: #02a8a2;
237
+ flex: 0 0 auto;
238
+ }
239
+ .grades-section {
240
+ margin-top: 16px;
241
+ background: #fff;
242
+ border-radius: 8px;
243
+ padding: 16px;
244
+ border: 2px solid #0c4da2;
245
+ }
246
+ .grades-section b {
247
+ display: block;
248
+ margin-bottom: 8px;
173
249
  }
174
250
  `; }
175
251
  get context() {
@@ -193,25 +269,18 @@ let KpiOverview = class KpiOverview extends PageView {
193
269
  return html `<div>에러: ${this.error?.message}</div>`;
194
270
  if (!this.kpiCategories.length)
195
271
  return html `<div>KPI 카테고리가 없습니다.</div>`;
272
+ const selctedY = this.kpiCategories?.[this.selectedGroup] || '';
196
273
  const group = this.kpiCategories[this.selectedGroup];
197
274
  const kpi = group?.kpis?.[this.selectedKpi];
198
- const md = kpi?.description || '각 지표의 주요 소개 내용을 마크다운으로 입력하세요.';
199
275
  return html `
200
276
  <div class="overview-container">
201
277
  <div class="overview-left">
202
278
  <div class="overview-title">KPI개요</div>
203
279
  <div class="overview-desc">
204
- 건설현장 KPI 관리 시스템은 프로젝트 성과를 측정하고 모니터링하여 효율적인 공정 관리를 지원합니다.<br />
205
- 주요 지표를 통해 현장 생산성, 품질, 안전 등을 실시간으로 파악할 수 있습니다.
280
+ 건설현장 KPI관리 시스템은 프로젝트 성과를 측정하고, 모니터링하여 효율적인 공정 관리를 지원합니다. 주요
281
+ 지표를 통해 현장 생산성, 품질, 안전 등을 실시간으로 파악할 수 있습니다.
206
282
  </div>
207
283
  </div>
208
- <div class="overview-right">
209
- <img
210
- class="overview-img"
211
- src="https://images.unsplash.com/photo-1464983953574-0892a716854b?auto=format&fit=crop&w=600&q=80"
212
- alt="건설현장 안전"
213
- />
214
- </div>
215
284
  </div>
216
285
  <div class="group-tabs">
217
286
  ${this.kpiCategories.map((g, i) => html `
@@ -229,27 +298,39 @@ let KpiOverview = class KpiOverview extends PageView {
229
298
  </div>
230
299
  <div class="main-content">
231
300
  <div class="markdown">
232
- <div style="font-size:1.5rem;font-weight:bold;margin-bottom:16px;">${kpi?.name || ''}</div>
233
- <div style="margin-bottom:16px;">${kpi?.formula ? html `<b>산식:</b> ${kpi.formula}` : ''}</div>
234
- <div .innerHTML=${marked(md)}></div>
235
- ${kpi?.grades && Array.isArray(kpi.grades) && kpi.grades.length > 0
236
- ? html `<div style="margin-top:32px;">
237
- <b>등급 구간</b>
238
- <ul>
239
- ${kpi.grades.map((g) => html `<li>${g.name}: ${g.minValue}~${g.maxValue} (${g.description || ''})</li>`)}
240
- </ul>
241
- </div>`
301
+ <div class="kpi-title">${kpi?.name || ''}</div>
302
+ <div>${kpi?.description || ''}</div>
303
+ ${kpi?.formula
304
+ ? html `
305
+ <div class="subtitle"><img src="/assets/images/green-calendar.svg" />주요산식</div>
306
+ <div class="kpi-formula">${kpi?.formula}</div>
307
+ `
242
308
  : ''}
243
309
  </div>
244
310
  <div class="toc">
245
- <div class="toc-title">목차</div>
311
+ <img src="/assets/images/green-calendar.svg" />
312
+ <div class="toc-title">${selctedY.name} 지표</div>
246
313
  <ul class="toc-list">
247
314
  ${group?.kpis?.map((k, i) => html `
248
- <li><button class="toc-btn" @click=${() => (this.selectedKpi = i)}>${k.name}</button></li>
315
+ <li>
316
+ <button class="toc-btn" ?selected=${i === this.selectedKpi} @click=${() => (this.selectedKpi = i)}>
317
+ ${k.name}
318
+ </button>
319
+ </li>
249
320
  `)}
250
321
  </ul>
251
322
  </div>
252
323
  </div>
324
+ ${kpi?.grades && Array.isArray(kpi.grades) && kpi.grades.length > 0
325
+ ? html `
326
+ <div class="grades-section">
327
+ <b>등급 구간</b>
328
+ <ul>
329
+ ${kpi.grades.map((g) => html `<li>${g.name}: ${g.minValue}~${g.maxValue} (${g.description || ''})</li>`)}
330
+ </ul>
331
+ </div>
332
+ `
333
+ : ''}
253
334
  `;
254
335
  }
255
336
  };
@@ -1 +1 @@
1
- {"version":3,"file":"kpi-overview.js","sourceRoot":"","sources":["../../../client/pages/kpi/kpi-overview.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, css, LitElement } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\nimport { marked } from 'marked'\nimport { PageView, store } from '@operato/shell'\nimport { i18next, localize } from '@operato/i18n'\nimport { client } from '@operato/graphql'\nimport gql from 'graphql-tag'\n\nconst GET_KPI_OVERVIEW = gql`\n query {\n kpiCategories: kpisLevel1 {\n id\n name\n description\n active\n kpis: children {\n id\n name\n description\n formula\n active\n grades\n }\n }\n }\n`\n\n@customElement('kpi-overview')\nexport class KpiOverview extends PageView {\n static styles = css`\n .overview-container {\n display: flex;\n gap: 32px;\n background: #f7f5fa;\n border-radius: 20px;\n padding: 32px;\n margin-bottom: 24px;\n align-items: stretch;\n }\n .overview-left {\n flex: 1;\n display: flex;\n flex-direction: column;\n justify-content: center;\n }\n .overview-title {\n font-size: 2.8rem;\n font-weight: bold;\n margin-bottom: 24px;\n }\n .overview-desc {\n font-size: 1.2rem;\n color: #222;\n line-height: 1.6;\n }\n .overview-right {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n .overview-img {\n width: 100%;\n max-width: 420px;\n border-radius: 12px;\n object-fit: cover;\n box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);\n }\n .group-tabs {\n display: flex;\n gap: 8px;\n margin: 0 0 16px 0;\n justify-content: center;\n }\n .group-tab {\n padding: 10px 28px;\n border-radius: 16px 16px 0 0;\n background: #eee;\n cursor: pointer;\n font-size: 1.1rem;\n font-weight: 500;\n border: none;\n outline: none;\n transition: background 0.2s;\n }\n .group-tab[selected] {\n background: #d6d6f7;\n color: #222;\n font-weight: bold;\n }\n .kpi-list {\n display: flex;\n gap: 8px;\n margin: 0 0 32px 0;\n justify-content: center;\n }\n .kpi-item {\n padding: 8px 20px;\n border-radius: 12px;\n background: #f5f5f5;\n cursor: pointer;\n font-size: 1rem;\n border: none;\n outline: none;\n transition: background 0.2s;\n }\n .kpi-item[selected] {\n background: #bdf;\n color: #222;\n font-weight: bold;\n }\n .main-content {\n display: flex;\n gap: 32px;\n margin-top: 32px;\n align-items: flex-start;\n justify-content: center;\n }\n .markdown {\n background: #fff;\n border-radius: 12px;\n padding: 40px 32px;\n min-height: 240px;\n flex: 2;\n font-size: 2rem;\n color: #222;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);\n }\n .toc {\n flex: 1;\n background: #faf9fd;\n border-radius: 12px;\n padding: 32px 24px;\n font-size: 1.1rem;\n color: #444;\n min-width: 220px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03);\n }\n .toc-title {\n font-weight: bold;\n margin-bottom: 12px;\n font-size: 1.2rem;\n }\n .toc-list {\n margin: 0 0 16px 0;\n padding: 0;\n list-style: none;\n }\n .toc-list li {\n margin-bottom: 6px;\n }\n .toc-btn {\n display: block;\n margin: 12px 0;\n padding: 8px 16px;\n border: 1px solid #aaa;\n border-radius: 8px;\n background: #fff;\n color: #333;\n cursor: pointer;\n font-size: 1rem;\n text-align: left;\n transition: background 0.2s;\n }\n .toc-btn:hover {\n background: #f0f0f0;\n }\n `\n\n get context() {\n return {\n title: i18next.t('title.kpi overview')\n }\n }\n\n @state() selectedGroup = 0\n @state() selectedKpi = 0\n @state() kpiCategories: any[] = []\n @state() loading = true\n @state() error: Error | null = null\n\n async firstUpdated() {\n try {\n const { data } = await client.query({ query: GET_KPI_OVERVIEW })\n this.kpiCategories = data.kpiCategories\n this.loading = false\n } catch (e) {\n this.error = e instanceof Error ? e : new Error(String(e))\n this.loading = false\n }\n }\n\n render() {\n if (this.error) return html`<div>에러: ${this.error?.message}</div>`\n if (!this.kpiCategories.length) return html`<div>KPI 카테고리가 없습니다.</div>`\n\n const group = this.kpiCategories[this.selectedGroup] as any\n const kpi = group?.kpis?.[this.selectedKpi] as any\n const md = kpi?.description || '각 지표의 주요 소개 내용을 마크다운으로 입력하세요.'\n\n return html`\n <div class=\"overview-container\">\n <div class=\"overview-left\">\n <div class=\"overview-title\">KPI개요</div>\n <div class=\"overview-desc\">\n 건설현장 KPI 관리 시스템은 프로젝트 성과를 측정하고 모니터링하여 효율적인 공정 관리를 지원합니다.<br />\n 주요 지표를 통해 현장 생산성, 품질, 안전 등을 실시간으로 파악할 수 있습니다.\n </div>\n </div>\n <div class=\"overview-right\">\n <img\n class=\"overview-img\"\n src=\"https://images.unsplash.com/photo-1464983953574-0892a716854b?auto=format&fit=crop&w=600&q=80\"\n alt=\"건설현장 안전\"\n />\n </div>\n </div>\n <div class=\"group-tabs\">\n ${this.kpiCategories.map(\n (g, i) => html`\n <button\n class=\"group-tab\"\n ?selected=${i === this.selectedGroup}\n @click=${() => {\n this.selectedGroup = i\n this.selectedKpi = 0\n }}\n >\n ${g.name}\n </button>\n `\n )}\n </div>\n <div class=\"main-content\">\n <div class=\"markdown\">\n <div style=\"font-size:1.5rem;font-weight:bold;margin-bottom:16px;\">${kpi?.name || ''}</div>\n <div style=\"margin-bottom:16px;\">${kpi?.formula ? html`<b>산식:</b> ${kpi.formula}` : ''}</div>\n <div .innerHTML=${marked(md)}></div>\n ${kpi?.grades && Array.isArray(kpi.grades) && kpi.grades.length > 0\n ? html`<div style=\"margin-top:32px;\">\n <b>등급 구간</b>\n <ul>\n ${kpi.grades.map(\n (g: any) => html`<li>${g.name}: ${g.minValue}~${g.maxValue} (${g.description || ''})</li>`\n )}\n </ul>\n </div>`\n : ''}\n </div>\n <div class=\"toc\">\n <div class=\"toc-title\">목차</div>\n <ul class=\"toc-list\">\n ${group?.kpis?.map(\n (k, i) => html`\n <li><button class=\"toc-btn\" @click=${() => (this.selectedKpi = i)}>${k.name}</button></li>\n `\n )}\n </ul>\n </div>\n </div>\n `\n }\n}\n"]}
1
+ {"version":3,"file":"kpi-overview.js","sourceRoot":"","sources":["../../../client/pages/kpi/kpi-overview.ts"],"names":[],"mappings":"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","sourcesContent":["import { html, css } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\nimport { PageView } from '@operato/shell'\nimport { i18next } from '@operato/i18n'\nimport { client } from '@operato/graphql'\nimport gql from 'graphql-tag'\n\nconst GET_KPI_OVERVIEW = gql`\n query {\n kpiCategories: kpisLevel1 {\n id\n name\n description\n active\n kpis: children {\n id\n name\n description\n formula\n active\n grades\n }\n }\n }\n`\n\n@customElement('kpi-overview')\nexport class KpiOverview extends PageView {\n static styles = css`\n :host {\n display: block;\n background: #f6f6f6;\n box-sizing: border-box;\n padding: 16px 24px;\n }\n .overview-container {\n display: block;\n margin: 8px 0 24px 0;\n }\n .overview-left {\n display: block;\n }\n .overview-title {\n color: #0c4da2;\n font-size: 1.125rem;\n font-weight: 700;\n letter-spacing: -0.05em;\n margin: 0 0 8px 0;\n }\n .overview-desc {\n font-size: 0.875rem;\n color: #212529;\n letter-spacing: -0.05em;\n line-height: 1;\n }\n .group-tabs {\n display: flex;\n margin: 8px 0 0 0;\n justify-content: flex-start;\n }\n .group-tab {\n padding: 10px 15px;\n border-radius: 8px 8px 0 0;\n background: rgba(46, 164, 223, 0.1);\n cursor: pointer;\n font-size: 1rem;\n font-weight: 400;\n border: none;\n outline: none;\n transition: background 0.2s;\n }\n .group-tab[selected] {\n background: #0c4da2;\n color: #ffffff;\n font-weight: 700;\n box-shadow: 1px 0 0 0 rgba(0, 0, 0, 0.1);\n }\n .tab-icon {\n display: inline-block;\n width: 20px;\n height: 20px;\n border-radius: 2px;\n background: #35618e;\n vertical-align: middle;\n }\n .group-tab[selected] .tab-icon {\n background: #ffffff;\n }\n .tab-label {\n margin-left: 6px;\n vertical-align: middle;\n }\n .kpi-list {\n display: flex;\n gap: 8px;\n margin: 0 0 32px 0;\n justify-content: center;\n }\n .kpi-item {\n padding: 8px 20px;\n border-radius: 12px;\n background: #f5f5f5;\n cursor: pointer;\n font-size: 1rem;\n border: none;\n outline: none;\n transition: background 0.2s;\n }\n .kpi-item[selected] {\n background: #bdf;\n color: #222;\n font-weight: bold;\n }\n .main-content {\n display: flex;\n gap: 0;\n align-items: stretch;\n justify-content: stretch;\n background: #fff;\n border: 2px solid #0c4da2;\n overflow: hidden;\n }\n .markdown {\n background: #fff;\n padding: 25px;\n min-height: 240px;\n flex: 1 1 auto;\n font-size: 0.875rem;\n color: #212529;\n }\n .markdown p {\n line-height: 1.7142857142857142;\n margin: 0 0 12px 0;\n }\n .toc {\n flex: 0 0 240px;\n background: linear-gradient(180deg, rgba(137, 204, 237, 0.2) 0%, rgba(137, 204, 237, 0) 100%);\n border-left: 1px solid rgba(46, 164, 223, 0.1);\n padding: 30px 15px;\n font-size: 1rem;\n color: #212529;\n }\n .toc img {\n width: 25px;\n height: 25px;\n }\n .submenu-header {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-bottom: 14px;\n }\n .submenu-icon {\n width: 25.5px;\n height: 28.67px;\n border-radius: 2px;\n background: #02a8a2;\n flex: 0 0 auto;\n }\n .submenu-title {\n font-weight: 700;\n font-size: 1.25rem;\n color: #0c4da2;\n }\n .toc-title {\n color: #0c4da2;\n font-size: 20px;\n font-weight: 700;\n line-height: 20px;\n margin-bottom: 15px;\n margin-top: 3px;\n }\n .toc-list {\n margin: 0 0 8px 0;\n padding: 0;\n list-style: none;\n }\n .toc-list li {\n margin-bottom: 10px;\n }\n .toc-btn {\n display: block;\n margin: 0;\n padding: 8px 0 8px 12px;\n border: none;\n border-left: 3px solid transparent;\n border-radius: 0;\n background: transparent;\n color: #212529;\n cursor: pointer;\n font-size: 1rem;\n text-align: left;\n transition: background 0.2s;\n }\n .toc-btn:hover {\n background: rgba(0, 0, 0, 0.03);\n }\n .toc-btn[selected] {\n border-left-color: #4cbb49;\n font-weight: 700;\n color: #212529;\n }\n .kpi-title {\n font-size: 1.125rem;\n font-weight: 700;\n margin-bottom: 8px;\n color: #02a8a2;\n }\n .kpi-formula {\n margin: 3px 0 20px 0;\n font-weight: 700;\n color: #212529;\n }\n .subtitle {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n margin-top: 20px;\n font-weight: 700;\n color: #0c4da2;\n }\n .subtitle img {\n width: 15px;\n height: 15px;\n }\n .subtitle-icon {\n width: 20px;\n height: 20px;\n border-radius: 2px;\n background: #02a8a2;\n flex: 0 0 auto;\n }\n .grades-section {\n margin-top: 16px;\n background: #fff;\n border-radius: 8px;\n padding: 16px;\n border: 2px solid #0c4da2;\n }\n .grades-section b {\n display: block;\n margin-bottom: 8px;\n }\n `\n\n get context() {\n return {\n title: i18next.t('title.kpi overview')\n }\n }\n\n @state() selectedGroup = 0\n @state() selectedKpi = 0\n @state() kpiCategories: any[] = []\n @state() loading = true\n @state() error: Error | null = null\n\n async firstUpdated() {\n try {\n const { data } = await client.query({ query: GET_KPI_OVERVIEW })\n this.kpiCategories = data.kpiCategories\n this.loading = false\n } catch (e) {\n this.error = e instanceof Error ? e : new Error(String(e))\n this.loading = false\n }\n }\n\n render() {\n if (this.error) return html`<div>에러: ${this.error?.message}</div>`\n if (!this.kpiCategories.length) return html`<div>KPI 카테고리가 없습니다.</div>`\n\n const selctedY = this.kpiCategories?.[this.selectedGroup] || ''\n const group = this.kpiCategories[this.selectedGroup] as any\n const kpi = group?.kpis?.[this.selectedKpi] as any\n\n return html`\n <div class=\"overview-container\">\n <div class=\"overview-left\">\n <div class=\"overview-title\">KPI개요</div>\n <div class=\"overview-desc\">\n 건설현장 KPI관리 시스템은 프로젝트 성과를 측정하고, 모니터링하여 효율적인 공정 관리를 지원합니다. 주요\n 지표를 통해 현장 생산성, 품질, 안전 등을 실시간으로 파악할 수 있습니다.\n </div>\n </div>\n </div>\n <div class=\"group-tabs\">\n ${this.kpiCategories.map(\n (g, i) => html`\n <button\n class=\"group-tab\"\n ?selected=${i === this.selectedGroup}\n @click=${() => {\n this.selectedGroup = i\n this.selectedKpi = 0\n }}\n >\n ${g.name}\n </button>\n `\n )}\n </div>\n <div class=\"main-content\">\n <div class=\"markdown\">\n <div class=\"kpi-title\">${kpi?.name || ''}</div>\n <div>${kpi?.description || ''}</div>\n ${kpi?.formula\n ? html`\n <div class=\"subtitle\"><img src=\"/assets/images/green-calendar.svg\" />주요산식</div>\n <div class=\"kpi-formula\">${kpi?.formula}</div>\n `\n : ''}\n </div>\n <div class=\"toc\">\n <img src=\"/assets/images/green-calendar.svg\" />\n <div class=\"toc-title\">${selctedY.name} 지표</div>\n <ul class=\"toc-list\">\n ${group?.kpis?.map(\n (k, i) => html`\n <li>\n <button class=\"toc-btn\" ?selected=${i === this.selectedKpi} @click=${() => (this.selectedKpi = i)}>\n ${k.name}\n </button>\n </li>\n `\n )}\n </ul>\n </div>\n </div>\n ${kpi?.grades && Array.isArray(kpi.grades) && kpi.grades.length > 0\n ? html`\n <div class=\"grades-section\">\n <b>등급 구간</b>\n <ul>\n ${kpi.grades.map(\n (g: any) => html`<li>${g.name}: ${g.minValue}~${g.maxValue} (${g.description || ''})</li>`\n )}\n </ul>\n </div>\n `\n : ''}\n `\n }\n}\n"]}