@dssp/dkpi 1.0.0-alpha.8 → 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.
Files changed (252) hide show
  1. package/KPI-STATISTICS-SERVICE.md +233 -0
  2. package/_index.html +0 -5
  3. package/assets/favicon.ico +0 -0
  4. package/assets/images/project-image.png +0 -0
  5. package/assets/manifest/apple-1024.png +0 -0
  6. package/assets/manifest/apple-120.png +0 -0
  7. package/assets/manifest/apple-152.png +0 -0
  8. package/assets/manifest/apple-167.png +0 -0
  9. package/assets/manifest/apple-180.png +0 -0
  10. package/assets/manifest/apple-touch-icon.png +0 -0
  11. package/assets/manifest/badge-128x128.png +0 -0
  12. package/assets/manifest/chrome-splashscreen-icon-384x384.png +0 -0
  13. package/assets/manifest/chrome-touch-icon-192x192.png +0 -0
  14. package/assets/manifest/icon-128x128.png +0 -0
  15. package/assets/manifest/icon-192x192.png +0 -0
  16. package/assets/manifest/icon-512x512.png +0 -0
  17. package/assets/manifest/icon-72x72.png +0 -0
  18. package/assets/manifest/icon-96x96.png +0 -0
  19. package/assets/manifest/image-metaog.png +0 -0
  20. package/assets/manifest/maskable_icon.png +0 -0
  21. package/assets/manifest/ms-icon-144x144.png +0 -0
  22. package/assets/manifest/ms-touch-icon-144x144-precomposed.png +0 -0
  23. package/assets/videos/intro.mp4 +0 -0
  24. package/config/config.development.js +2 -1
  25. package/config/config.production.js +2 -1
  26. package/dist-client/bootstrap.d.ts +1 -0
  27. package/dist-client/bootstrap.js +75 -4
  28. package/dist-client/bootstrap.js.map +1 -1
  29. package/dist-client/components/kpi-2d-lookup-chart.d.ts +43 -0
  30. package/dist-client/components/kpi-2d-lookup-chart.js +253 -0
  31. package/dist-client/components/kpi-2d-lookup-chart.js.map +1 -0
  32. package/dist-client/components/kpi-boxplot-chart.d.ts +24 -0
  33. package/dist-client/components/kpi-boxplot-chart.js +291 -0
  34. package/dist-client/components/kpi-boxplot-chart.js.map +1 -0
  35. package/dist-client/components/kpi-lookup-chart.d.ts +53 -0
  36. package/dist-client/components/kpi-lookup-chart.js +430 -0
  37. package/dist-client/components/kpi-lookup-chart.js.map +1 -0
  38. package/dist-client/components/kpi-mini-trend-chart.d.ts +14 -0
  39. package/dist-client/components/kpi-mini-trend-chart.js +148 -0
  40. package/dist-client/components/kpi-mini-trend-chart.js.map +1 -0
  41. package/dist-client/components/kpi-radar-chart.d.ts +17 -0
  42. package/dist-client/components/kpi-radar-chart.js +259 -0
  43. package/dist-client/components/kpi-radar-chart.js.map +1 -0
  44. package/dist-client/components/kpi-single-boxplot-chart.d.ts +25 -0
  45. package/dist-client/components/kpi-single-boxplot-chart.js +398 -0
  46. package/dist-client/components/kpi-single-boxplot-chart.js.map +1 -0
  47. package/dist-client/components/kpi-trend-chart.d.ts +25 -0
  48. package/dist-client/components/kpi-trend-chart.js +220 -0
  49. package/dist-client/components/kpi-trend-chart.js.map +1 -0
  50. package/dist-client/components/sv-pagenation-control.d.ts +18 -0
  51. package/dist-client/components/sv-pagenation-control.js +142 -0
  52. package/dist-client/components/sv-pagenation-control.js.map +1 -0
  53. package/dist-client/entries/auth/checkin.d.ts +38 -0
  54. package/dist-client/entries/auth/checkin.js +546 -0
  55. package/dist-client/entries/auth/checkin.js.map +1 -0
  56. package/dist-client/google-map/common-google-map.d.ts +35 -0
  57. package/dist-client/google-map/common-google-map.js +345 -0
  58. package/dist-client/google-map/common-google-map.js.map +1 -0
  59. package/dist-client/google-map/google-map-loader.d.ts +6 -0
  60. package/dist-client/google-map/google-map-loader.js +23 -0
  61. package/dist-client/google-map/google-map-loader.js.map +1 -0
  62. package/dist-client/icons/menu-icons.d.ts +6 -0
  63. package/dist-client/icons/menu-icons.js +42 -0
  64. package/dist-client/icons/menu-icons.js.map +1 -1
  65. package/dist-client/pages/component/project-update-header.d.ts +1 -0
  66. package/dist-client/pages/component/project-update-header.js +127 -0
  67. package/dist-client/pages/component/project-update-header.js.map +1 -0
  68. package/dist-client/pages/kpi-admin/dssp-kpi-list-page.d.ts +22 -0
  69. package/dist-client/pages/kpi-admin/dssp-kpi-list-page.js +57 -0
  70. package/dist-client/pages/kpi-admin/dssp-kpi-list-page.js.map +1 -0
  71. package/dist-client/pages/kpi-admin/dssp-kpi-overview.d.ts +46 -0
  72. package/dist-client/pages/kpi-admin/dssp-kpi-overview.js +378 -0
  73. package/dist-client/pages/kpi-admin/dssp-kpi-overview.js.map +1 -0
  74. package/dist-client/pages/kpi-admin/kpi-grade-2d-editor.d.ts +20 -0
  75. package/dist-client/pages/kpi-admin/kpi-grade-2d-editor.js +445 -0
  76. package/dist-client/pages/kpi-admin/kpi-grade-2d-editor.js.map +1 -0
  77. package/dist-client/pages/kpi-admin/kpi-system-guide.d.ts +18 -0
  78. package/dist-client/pages/kpi-admin/kpi-system-guide.js +535 -0
  79. package/dist-client/pages/kpi-admin/kpi-system-guide.js.map +1 -0
  80. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.d.ts +18 -0
  81. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js +259 -0
  82. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js.map +1 -0
  83. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.d.ts +22 -0
  84. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js +346 -0
  85. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js.map +1 -0
  86. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.d.ts +26 -0
  87. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js +433 -0
  88. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js.map +1 -0
  89. package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.d.ts +8 -0
  90. package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.js +78 -0
  91. package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.js.map +1 -0
  92. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.d.ts +38 -0
  93. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js +851 -0
  94. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js.map +1 -0
  95. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.d.ts +42 -0
  96. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js +316 -0
  97. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js.map +1 -0
  98. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.d.ts +28 -0
  99. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js +497 -0
  100. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js.map +1 -0
  101. package/dist-client/pages/kpi-dashboard/kpi-alert-panel.d.ts +18 -0
  102. package/dist-client/pages/kpi-dashboard/kpi-alert-panel.js +131 -0
  103. package/dist-client/pages/kpi-dashboard/kpi-alert-panel.js.map +1 -0
  104. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.d.ts +52 -0
  105. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js +798 -0
  106. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js.map +1 -0
  107. package/dist-client/pages/kpi-dashboard/kpi-dashboard.d.ts +63 -0
  108. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js +1089 -0
  109. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js.map +1 -0
  110. package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.d.ts +12 -0
  111. package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.js +82 -0
  112. package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.js.map +1 -0
  113. package/dist-client/pages/kpi-dashboard/kpi-history-viewer.d.ts +11 -0
  114. package/dist-client/pages/kpi-dashboard/kpi-history-viewer.js +65 -0
  115. package/dist-client/pages/kpi-dashboard/kpi-history-viewer.js.map +1 -0
  116. package/dist-client/pages/kpi-dashboard/kpi-list-summary.d.ts +13 -0
  117. package/dist-client/pages/kpi-dashboard/kpi-list-summary.js +115 -0
  118. package/dist-client/pages/kpi-dashboard/kpi-list-summary.js.map +1 -0
  119. package/dist-client/pages/kpi-dashboard/kpi-performance-summary.d.ts +15 -0
  120. package/dist-client/pages/kpi-dashboard/kpi-performance-summary.js +147 -0
  121. package/dist-client/pages/kpi-dashboard/kpi-performance-summary.js.map +1 -0
  122. package/dist-client/pages/kpi-dashboard/kpi-value-entry.d.ts +7 -0
  123. package/dist-client/pages/kpi-dashboard/kpi-value-entry.js +86 -0
  124. package/dist-client/pages/kpi-dashboard/kpi-value-entry.js.map +1 -0
  125. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.d.ts +57 -0
  126. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js +719 -0
  127. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js.map +1 -0
  128. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.d.ts +23 -0
  129. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js +76 -0
  130. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js.map +1 -0
  131. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.d.ts +68 -0
  132. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js +394 -0
  133. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js.map +1 -0
  134. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.d.ts +12 -0
  135. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js +174 -0
  136. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js.map +1 -0
  137. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.d.ts +40 -0
  138. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js +190 -0
  139. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js.map +1 -0
  140. package/dist-client/pages/kpi-value/kpi-value-importer.d.ts +23 -0
  141. package/dist-client/pages/kpi-value/kpi-value-importer.js +93 -0
  142. package/dist-client/pages/kpi-value/kpi-value-importer.js.map +1 -0
  143. package/dist-client/pages/kpi-value/kpi-value-list-page.d.ts +71 -0
  144. package/dist-client/pages/kpi-value/kpi-value-list-page.js +454 -0
  145. package/dist-client/pages/kpi-value/kpi-value-list-page.js.map +1 -0
  146. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.d.ts +61 -0
  147. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js +766 -0
  148. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js.map +1 -0
  149. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.d.ts +16 -0
  150. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js +341 -0
  151. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js.map +1 -0
  152. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.d.ts +21 -0
  153. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.js +335 -0
  154. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.js.map +1 -0
  155. package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.d.ts +42 -0
  156. package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.js +641 -0
  157. package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.js.map +1 -0
  158. package/dist-client/pages/sv-project-complete.d.ts +25 -0
  159. package/dist-client/pages/sv-project-complete.js +263 -0
  160. package/dist-client/pages/sv-project-complete.js.map +1 -0
  161. package/dist-client/pages/sv-project-completed-list.d.ts +27 -0
  162. package/dist-client/pages/sv-project-completed-list.js +416 -0
  163. package/dist-client/pages/sv-project-completed-list.js.map +1 -0
  164. package/dist-client/pages/sv-project-detail.d.ts +58 -0
  165. package/dist-client/pages/sv-project-detail.js +1418 -0
  166. package/dist-client/pages/sv-project-detail.js.map +1 -0
  167. package/dist-client/pages/sv-project-list.d.ts +174 -0
  168. package/dist-client/pages/sv-project-list.js +578 -0
  169. package/dist-client/pages/sv-project-list.js.map +1 -0
  170. package/dist-client/pages/sv-project-update.d.ts +86 -0
  171. package/dist-client/pages/sv-project-update.js +1331 -0
  172. package/dist-client/pages/sv-project-update.js.map +1 -0
  173. package/dist-client/pages/sv-user-management.d.ts +1 -0
  174. package/dist-client/pages/sv-user-management.js +5 -0
  175. package/dist-client/pages/sv-user-management.js.map +1 -1
  176. package/dist-client/route.d.ts +1 -1
  177. package/dist-client/route.js +50 -2
  178. package/dist-client/route.js.map +1 -1
  179. package/dist-client/shared/complete-api.d.ts +37 -0
  180. package/dist-client/shared/complete-api.js +224 -0
  181. package/dist-client/shared/complete-api.js.map +1 -0
  182. package/dist-client/shared/func.d.ts +2 -0
  183. package/dist-client/shared/func.js +22 -0
  184. package/dist-client/shared/func.js.map +1 -0
  185. package/dist-client/shared/integration-fetch.d.ts +35 -0
  186. package/dist-client/shared/integration-fetch.js +53 -0
  187. package/dist-client/shared/integration-fetch.js.map +1 -0
  188. package/dist-client/themes/dark.css +24 -24
  189. package/dist-client/themes/light.css +23 -23
  190. package/dist-client/tsconfig.tsbuildinfo +1 -1
  191. package/dist-client/viewparts/menu-tools.d.ts +40 -5
  192. package/dist-client/viewparts/menu-tools.js +292 -43
  193. package/dist-client/viewparts/menu-tools.js.map +1 -1
  194. package/dist-server/index.d.ts +2 -0
  195. package/dist-server/index.js +5 -0
  196. package/dist-server/index.js.map +1 -1
  197. package/dist-server/migrations/index.d.ts +1 -0
  198. package/dist-server/migrations/index.js +12 -0
  199. package/dist-server/migrations/index.js.map +1 -0
  200. package/dist-server/scripts/calculate-kpi-scores.d.ts +10 -0
  201. package/dist-server/scripts/calculate-kpi-scores.js +333 -0
  202. package/dist-server/scripts/calculate-kpi-scores.js.map +1 -0
  203. package/dist-server/scripts/load-grade-data-migration.d.ts +14 -0
  204. package/dist-server/scripts/load-grade-data-migration.js +279 -0
  205. package/dist-server/scripts/load-grade-data-migration.js.map +1 -0
  206. package/dist-server/scripts/propagate-parent-kpi-values.d.ts +14 -0
  207. package/dist-server/scripts/propagate-parent-kpi-values.js +786 -0
  208. package/dist-server/scripts/propagate-parent-kpi-values.js.map +1 -0
  209. package/dist-server/scripts/recalculate-by-project-name.d.ts +2 -0
  210. package/dist-server/scripts/recalculate-by-project-name.js +72 -0
  211. package/dist-server/scripts/recalculate-by-project-name.js.map +1 -0
  212. package/dist-server/service/index.d.ts +4 -0
  213. package/dist-server/service/index.js +20 -0
  214. package/dist-server/service/index.js.map +1 -0
  215. package/dist-server/service/kpi-metric-value/index.d.ts +4 -0
  216. package/dist-server/service/kpi-metric-value/index.js +8 -0
  217. package/dist-server/service/kpi-metric-value/index.js.map +1 -0
  218. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.d.ts +166 -0
  219. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js +951 -0
  220. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js.map +1 -0
  221. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.d.ts +7 -0
  222. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js +59 -0
  223. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js.map +1 -0
  224. package/dist-server/service/kpi-stat/index.d.ts +4 -0
  225. package/dist-server/service/kpi-stat/index.js +8 -0
  226. package/dist-server/service/kpi-stat/index.js.map +1 -0
  227. package/dist-server/service/kpi-stat/kpi-stat-query.d.ts +12 -0
  228. package/dist-server/service/kpi-stat/kpi-stat-query.js +663 -0
  229. package/dist-server/service/kpi-stat/kpi-stat-query.js.map +1 -0
  230. package/dist-server/service/kpi-stat/kpi-stat-types.d.ts +32 -0
  231. package/dist-server/service/kpi-stat/kpi-stat-types.js +180 -0
  232. package/dist-server/service/kpi-stat/kpi-stat-types.js.map +1 -0
  233. package/dist-server/service/kpi-value/index.d.ts +3 -0
  234. package/dist-server/service/kpi-value/index.js +7 -0
  235. package/dist-server/service/kpi-value/index.js.map +1 -0
  236. package/dist-server/service/kpi-value/kpi-value-query.d.ts +8 -0
  237. package/dist-server/service/kpi-value/kpi-value-query.js +63 -0
  238. package/dist-server/service/kpi-value/kpi-value-query.js.map +1 -0
  239. package/dist-server/tsconfig.tsbuildinfo +1 -1
  240. package/kpi-module-service-tests.md +1286 -0
  241. package/kpi-module-test-report.md +676 -0
  242. package/kpi-module-unit-test-detailed-report.md +925 -0
  243. package/kpi-module-unit-tests-detailed.md +1452 -0
  244. package/package.json +69 -55
  245. package/recalculate-batch.sh +64 -0
  246. package/recalculate-projects-range.sh +98 -0
  247. package/schema.graphql +3423 -462
  248. package/things-factory.config.js +12 -1
  249. package/translations/en.json +5 -1
  250. package/translations/ko.json +5 -1
  251. package/views/auth-page.html +0 -1
  252. package/views/public/home.html +0 -1
@@ -0,0 +1,1331 @@
1
+ import { __decorate, __metadata } from "tslib";
2
+ import '@material/web/icon/icon.js';
3
+ import '@material/web/button/elevated-button.js';
4
+ import '@material/web/textfield/outlined-text-field.js';
5
+ import { PageView } from '@operato/shell';
6
+ import { css, html } from 'lit';
7
+ import { customElement, state } from 'lit/decorators.js';
8
+ import { ScopedElementsMixin } from '@open-wc/scoped-elements';
9
+ import { client } from '@operato/graphql';
10
+ import { notify } from '@operato/layout';
11
+ import { OxPrompt } from '@operato/popup';
12
+ import gql from 'graphql-tag';
13
+ import { SITE_TYPE_DISPLAY } from '@dssp/project/dist-client/pages/project/project-list';
14
+ import '@dssp/project/dist-client/pages/lib/select2-component';
15
+ import './component/project-update-header';
16
+ import { isProjectTypeDomain, tenantHeaders } from '@dssp/project/dist-client/shared/domain-context';
17
+ let SvProjectUpdate = class SvProjectUpdate extends ScopedElementsMixin(PageView) {
18
+ constructor() {
19
+ super(...arguments);
20
+ this.defaultProject = {
21
+ name: '',
22
+ documentNaming: '',
23
+ buildingComplex: {
24
+ address: '',
25
+ area: 0,
26
+ constructionCompany: '',
27
+ clientCompany: '',
28
+ supervisoryCompany: '',
29
+ designCompany: '',
30
+ constructionType: '',
31
+ buildings: []
32
+ }
33
+ };
34
+ this.projectId = '';
35
+ this.project = Object.assign({}, this.defaultProject);
36
+ this.selectedValues = [];
37
+ this.overallConstructorList = [];
38
+ this.taskConstructorList = [];
39
+ this.taskSupervisoryList = [];
40
+ this.overallSupervisoryList = [];
41
+ this.collectingBasicInfo = false;
42
+ /**
43
+ * 완료(COMPLETED) 상태 프로젝트를 진행중(ONGOING) 으로 되돌림.
44
+ * ProjectPatch 의 state 필드만 갱신 — 다른 폼 필드는 건드리지 않음.
45
+ */
46
+ this._onUnfinalize = async () => {
47
+ var _a, _b;
48
+ if (!((_a = this.project) === null || _a === void 0 ? void 0 : _a.id))
49
+ return;
50
+ const ok = await OxPrompt.open({
51
+ title: '완료 상태 해제',
52
+ confirmButton: { text: '확인' },
53
+ cancelButton: { text: '취소' }
54
+ });
55
+ if (!ok)
56
+ return;
57
+ const response = await client.mutate({
58
+ mutation: gql `
59
+ mutation UpdateProject($project: ProjectPatch!) {
60
+ updateProject(project: $project) {
61
+ id
62
+ state
63
+ }
64
+ }
65
+ `,
66
+ variables: { project: { id: this.project.id, state: 'ONGOING' } },
67
+ context: { headers: tenantHeaders((_b = this.project) === null || _b === void 0 ? void 0 : _b.code) }
68
+ });
69
+ if (!response.errors) {
70
+ notify({ message: '진행중 상태로 변경되었습니다.' });
71
+ await this.initProject(this.project.id);
72
+ }
73
+ };
74
+ }
75
+ get context() {
76
+ var _a, _b;
77
+ return {
78
+ title: !isProjectTypeDomain() && ((_a = this.project) === null || _a === void 0 ? void 0 : _a.name)
79
+ ? `프로젝트 정보 관리 - ${(_b = this.project) === null || _b === void 0 ? void 0 : _b.name}`
80
+ : '프로젝트 정보 관리'
81
+ };
82
+ }
83
+ render() {
84
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25;
85
+ return html `
86
+ <dkpi-project-update-header
87
+ .projectId=${this.project.id || ''}
88
+ .projectState=${this.project.state || ''}
89
+ title="프로젝트 정보 관리"
90
+ @custom-click=${this._saveProject}
91
+ @unfinalize=${this._onUnfinalize}
92
+ ></dkpi-project-update-header>
93
+
94
+ <div body>
95
+ <div project-info>
96
+ <h3>기본 정보</h3>
97
+ <div row>
98
+ <span>프로젝트명</span>
99
+ <span
100
+ ><md-outlined-text-field
101
+ type="text"
102
+ name="name"
103
+ project
104
+ .value=${this.project.name || ''}
105
+ @input=${this._onInputChange}
106
+ ></md-outlined-text-field>
107
+ </span>
108
+ </div>
109
+ <div row>
110
+ <span>프로젝트 문서 네이밍</span>
111
+ <span>
112
+ <md-outlined-text-field
113
+ type="text"
114
+ name="documentNaming"
115
+ project
116
+ .value=${this.project.documentNaming || ''}
117
+ @input=${this._onInputChange}
118
+ placeholder="프로젝트 문서 네이밍"
119
+ ></md-outlined-text-field>
120
+ </span>
121
+ </div>
122
+ <div row>
123
+ <span></span>
124
+ <span>
125
+ <div document-preview>📄 문서 생성 예시: <br />${this.project.documentNaming} 검측 제01-00001호</div>
126
+ </span>
127
+ </div>
128
+ <div row>
129
+ <span>프로젝트 주소</span>
130
+ <span class="address-row">
131
+ <md-outlined-text-field
132
+ type="text"
133
+ name="address"
134
+ building-complex
135
+ .value=${((_b = (_a = this.project) === null || _a === void 0 ? void 0 : _a.buildingComplex) === null || _b === void 0 ? void 0 : _b.address) || ''}
136
+ @input=${this._onInputChange}
137
+ ></md-outlined-text-field>
138
+ <md-elevated-button
139
+ class="info-link-btn"
140
+ ?disabled=${this.collectingBasicInfo}
141
+ @click=${() => this._collectBasicInfo()}
142
+ title="입력된 주소로 세움터 건축물대장 정보를 가져와 폼을 채웁니다"
143
+ >
144
+ ${this.collectingBasicInfo ? '연동 중…' : '🔗 정보 연동'}
145
+ </md-elevated-button>
146
+ </span>
147
+ </div>
148
+ <div row>
149
+ <span>위도</span>
150
+ <span>
151
+ <md-outlined-text-field
152
+ type="text"
153
+ name="latitude"
154
+ numeric
155
+ building-complex
156
+ .value=${((_e = (_d = (_c = this.project) === null || _c === void 0 ? void 0 : _c.buildingComplex) === null || _d === void 0 ? void 0 : _d.latitude) === null || _e === void 0 ? void 0 : _e.toString()) || ''}
157
+ @input=${this._onInputChange}
158
+ ></md-outlined-text-field>
159
+ </span>
160
+ </div>
161
+ <div row>
162
+ <span>경도</span>
163
+ <span>
164
+ <md-outlined-text-field
165
+ type="text"
166
+ name="longitude"
167
+ numeric
168
+ building-complex
169
+ .value=${((_h = (_g = (_f = this.project) === null || _f === void 0 ? void 0 : _f.buildingComplex) === null || _g === void 0 ? void 0 : _g.longitude) === null || _h === void 0 ? void 0 : _h.toString()) || ''}
170
+ @input=${this._onInputChange}
171
+ ></md-outlined-text-field>
172
+ </span>
173
+ </div>
174
+ <div row>
175
+ <span>연 면적</span>
176
+ <span align-end
177
+ ><md-outlined-text-field
178
+ type="text"
179
+ name="area"
180
+ numeric
181
+ building-complex
182
+ .value=${((_l = (_k = (_j = this.project) === null || _j === void 0 ? void 0 : _j.buildingComplex) === null || _k === void 0 ? void 0 : _k.area) === null || _l === void 0 ? void 0 : _l.toString()) || ''}
183
+ @input=${this._onInputChange}
184
+ suffix-text="㎡"
185
+ ></md-outlined-text-field>
186
+ </span>
187
+ </div>
188
+
189
+ <div row>
190
+ <span>건폐율</span>
191
+ <span align-end>
192
+ <md-outlined-text-field
193
+ type="text"
194
+ name="coverageRatio"
195
+ numeric
196
+ building-complex
197
+ .value=${((_p = (_o = (_m = this.project) === null || _m === void 0 ? void 0 : _m.buildingComplex) === null || _o === void 0 ? void 0 : _o.coverageRatio) === null || _p === void 0 ? void 0 : _p.toString()) || ''}
198
+ @input=${this._onInputChange}
199
+ suffix-text="%"
200
+ ></md-outlined-text-field>
201
+ </span>
202
+ </div>
203
+ <div row>
204
+ <span>용적률</span>
205
+ <span align-end>
206
+ <md-outlined-text-field
207
+ type="text"
208
+ name="floorAreaRatio"
209
+ numeric
210
+ building-complex
211
+ .value=${((_s = (_r = (_q = this.project) === null || _q === void 0 ? void 0 : _q.buildingComplex) === null || _r === void 0 ? void 0 : _r.floorAreaRatio) === null || _s === void 0 ? void 0 : _s.toString()) || ''}
212
+ @input=${this._onInputChange}
213
+ suffix-text="%"
214
+ ></md-outlined-text-field>
215
+ </span>
216
+ </div>
217
+
218
+ <div row>
219
+ <span>현장유형</span>
220
+ <span
221
+ ><md-filled-select name="siteType" building-complex @change=${this._onInputChange}>
222
+ ${Object.entries(SITE_TYPE_DISPLAY).map(([key, display]) => {
223
+ var _a, _b;
224
+ return html `
225
+ <md-select-option ?selected=${((_b = (_a = this.project) === null || _a === void 0 ? void 0 : _a.buildingComplex) === null || _b === void 0 ? void 0 : _b.siteType) === key} .value=${key}>
226
+ <div slot="headline">${display}</div>
227
+ </md-select-option>
228
+ `;
229
+ })}
230
+ </md-filled-select>
231
+ </span>
232
+ </div>
233
+
234
+ <div row>
235
+ <span>착공일정 ~ 준공일정</span>
236
+ <span
237
+ ><input
238
+ type="date"
239
+ name="startDate"
240
+ project
241
+ .value=${this.project.startDate || ''}
242
+ @input=${this._onInputChange}
243
+ max="9999-12-31"
244
+ />
245
+ ~
246
+ <input
247
+ type="date"
248
+ name="endDate"
249
+ project
250
+ .value=${this.project.endDate || ''}
251
+ @input=${this._onInputChange}
252
+ max="9999-12-31"
253
+ />
254
+ </span>
255
+ </div>
256
+ <div row>
257
+ <span>발주처</span>
258
+ <span
259
+ ><md-outlined-text-field
260
+ type="text"
261
+ name="clientCompany"
262
+ building-complex
263
+ .value=${((_u = (_t = this.project) === null || _t === void 0 ? void 0 : _t.buildingComplex) === null || _u === void 0 ? void 0 : _u.clientCompany) || ''}
264
+ @input=${this._onInputChange}
265
+ ></md-outlined-text-field>
266
+ </span>
267
+ </div>
268
+ <div row>
269
+ <span>건설사</span>
270
+ <span
271
+ ><md-outlined-text-field
272
+ type="text"
273
+ name="constructionCompany"
274
+ building-complex
275
+ .value=${((_w = (_v = this.project) === null || _v === void 0 ? void 0 : _v.buildingComplex) === null || _w === void 0 ? void 0 : _w.constructionCompany) || ''}
276
+ @input=${this._onInputChange}
277
+ ></md-outlined-text-field>
278
+ </span>
279
+ </div>
280
+ <div row>
281
+ <span>설계사</span>
282
+ <span
283
+ ><md-outlined-text-field
284
+ type="text"
285
+ name="designCompany"
286
+ building-complex
287
+ .value=${((_y = (_x = this.project) === null || _x === void 0 ? void 0 : _x.buildingComplex) === null || _y === void 0 ? void 0 : _y.designCompany) || ''}
288
+ @input=${this._onInputChange}
289
+ ></md-outlined-text-field>
290
+ </span>
291
+ </div>
292
+ <div row>
293
+ <span>감리사</span>
294
+ <span
295
+ ><md-outlined-text-field
296
+ type="text"
297
+ name="supervisoryCompany"
298
+ building-complex
299
+ .value=${((_0 = (_z = this.project) === null || _z === void 0 ? void 0 : _z.buildingComplex) === null || _0 === void 0 ? void 0 : _0.supervisoryCompany) || ''}
300
+ @input=${this._onInputChange}
301
+ ></md-outlined-text-field>
302
+ </span>
303
+ </div>
304
+ <div row>
305
+ <span>건설구분</span>
306
+ <span
307
+ ><md-outlined-text-field
308
+ type="text"
309
+ name="constructionType"
310
+ building-complex
311
+ .value=${((_2 = (_1 = this.project) === null || _1 === void 0 ? void 0 : _1.buildingComplex) === null || _2 === void 0 ? void 0 : _2.constructionType) || ''}
312
+ @input=${this._onInputChange}
313
+ ></md-outlined-text-field>
314
+ </span>
315
+ </div>
316
+ <div row>
317
+ <span>구조 유형</span>
318
+ <span
319
+ ><md-outlined-text-field
320
+ type="text"
321
+ name="structureType"
322
+ building-complex
323
+ .value=${((_4 = (_3 = this.project) === null || _3 === void 0 ? void 0 : _3.buildingComplex) === null || _4 === void 0 ? void 0 : _4.structureType) || ''}
324
+ @input=${this._onInputChange}
325
+ ></md-outlined-text-field>
326
+ </span>
327
+ </div>
328
+
329
+ <div row>
330
+ <span>대표사진 업로드</span>
331
+ <span>
332
+ <ox-input-image
333
+ name="mainPhoto"
334
+ value=${((_6 = (_5 = this.project) === null || _5 === void 0 ? void 0 : _5.mainPhoto) === null || _6 === void 0 ? void 0 : _6.fullpath) || ''}
335
+ @change=${this.onCreateAttachment.bind(this)}
336
+ ></ox-input-image>
337
+ </span>
338
+ </div>
339
+ <div row>
340
+ <span>공사금액</span>
341
+ <span
342
+ ><md-outlined-text-field
343
+ type="text"
344
+ name="constructionCost"
345
+ numeric
346
+ building-complex
347
+ .value=${((_9 = (_8 = (_7 = this.project) === null || _7 === void 0 ? void 0 : _7.buildingComplex) === null || _8 === void 0 ? void 0 : _8.constructionCost) === null || _9 === void 0 ? void 0 : _9.toString()) || ''}
348
+ @input=${this._onInputChange}
349
+ suffix-text="억원"
350
+ ></md-outlined-text-field>
351
+ </span>
352
+ </div>
353
+ <div row>
354
+ <span>투입 인력</span>
355
+ <span
356
+ ><md-outlined-text-field
357
+ type="text"
358
+ name="workerCount"
359
+ numeric
360
+ building-complex
361
+ .value=${((_12 = (_11 = (_10 = this.project) === null || _10 === void 0 ? void 0 : _10.buildingComplex) === null || _11 === void 0 ? void 0 : _11.workerCount) === null || _12 === void 0 ? void 0 : _12.toString()) || ''}
362
+ @input=${this._onInputChange}
363
+ suffix-text="명"
364
+ ></md-outlined-text-field>
365
+ </span>
366
+ </div>
367
+ </div>
368
+
369
+ <div detail-info>
370
+ <div>
371
+ <h3>테넌트 관리</h3>
372
+ <div row>
373
+ <span>관리번호</span>
374
+ <span>
375
+ ${this.project.tenantDomain
376
+ ? html `<a href=${`/project/${this.project.code}/`} target="_blank" title="테넌트로 이동"
377
+ >${this.project.code} <md-icon style="font-size:14px;vertical-align:middle">open_in_new</md-icon></a
378
+ >`
379
+ : this.project.code
380
+ ? html `${this.project.code}`
381
+ : html `<span style="color:#999">미발번 (승격 시 자동 부여)</span>`}
382
+ </span>
383
+ </div>
384
+ <div row>
385
+ <span>현재 상태</span>
386
+ <span>
387
+ ${this.project.tenantDomain
388
+ ? html `<b style="color:#42b382">활성 테넌트</b>`
389
+ : this.project.code
390
+ ? html `<b style="color:#aa6633">강등됨</b> (재승격 시 ${this.project.code} 재사용)`
391
+ : html `<b style="color:#999">미승격</b>`}
392
+ </span>
393
+ </div>
394
+ <div row>
395
+ <span></span>
396
+ <span tenant-actions>
397
+ ${this.project.tenantDomain
398
+ ? html `<md-elevated-button @click=${this._demoteTenant} ?disabled=${!this.project.id}>
399
+ <md-icon slot="icon">link_off</md-icon>테넌트 강등
400
+ </md-elevated-button>`
401
+ : html `<md-elevated-button green @click=${this._promoteTenant} ?disabled=${!this.project.id}>
402
+ <md-icon slot="icon">verified</md-icon>테넌트 승격
403
+ </md-elevated-button>`}
404
+ </span>
405
+ </div>
406
+ </div>
407
+
408
+ <div project>
409
+ <h3>프로젝트 현황</h3>
410
+ <div row separate-container>
411
+ <div>
412
+ <span>전체 진행현황</span>
413
+ <span>
414
+ <md-outlined-text-field
415
+ type="text"
416
+ numeric
417
+ project
418
+ name="totalProgress"
419
+ .value=${((_13 = this.project.totalProgress) === null || _13 === void 0 ? void 0 : _13.toString()) || ''}
420
+ @input=${this._onInputChange}
421
+ suffix-text="%"
422
+ ></md-outlined-text-field>
423
+ </span>
424
+ </div>
425
+ <div>
426
+ <span>검측/통과비율</span>
427
+ <span>
428
+ <md-outlined-text-field
429
+ type="text"
430
+ numeric
431
+ project
432
+ name="inspPassRate"
433
+ .value=${((_14 = this.project.inspPassRate) === null || _14 === void 0 ? void 0 : _14.toString()) || ''}
434
+ @input=${this._onInputChange}
435
+ suffix-text="%"
436
+ ></md-outlined-text-field>
437
+ </span>
438
+ </div>
439
+ </div>
440
+ <div row separate-container>
441
+ <div>
442
+ <span>주간 진행현황</span>
443
+ <span>
444
+ <md-outlined-text-field
445
+ type="text"
446
+ numeric
447
+ project
448
+ name="weeklyProgress"
449
+ .value=${((_15 = this.project.weeklyProgress) === null || _15 === void 0 ? void 0 : _15.toString()) || ''}
450
+ @input=${this._onInputChange}
451
+ suffix-text="%"
452
+ ></md-outlined-text-field>
453
+ </span>
454
+ </div>
455
+ <div>
456
+ <span>로봇작업진행율</span>
457
+ <span>
458
+ <md-outlined-text-field
459
+ type="text"
460
+ numeric
461
+ project
462
+ name="robotProgressRate"
463
+ .value=${((_16 = this.project.robotProgressRate) === null || _16 === void 0 ? void 0 : _16.toString()) || ''}
464
+ @input=${this._onInputChange}
465
+ suffix-text="%"
466
+ ></md-outlined-text-field>
467
+ </span>
468
+ </div>
469
+ </div>
470
+ <div row separate-container>
471
+ <div>
472
+ <span>KPI</span>
473
+ <span
474
+ ><md-outlined-text-field
475
+ type="text"
476
+ numeric
477
+ project
478
+ name="kpi"
479
+ .value=${((_17 = this.project.kpi) === null || _17 === void 0 ? void 0 : _17.toString()) || ''}
480
+ @input=${this._onInputChange}
481
+ suffix-text="%"
482
+ ></md-outlined-text-field>
483
+ </span>
484
+ </div>
485
+ <div>
486
+ <span>구조안전도</span>
487
+ <span
488
+ ><md-outlined-text-field
489
+ type="text"
490
+ numeric
491
+ project
492
+ name="structuralSafetyRate"
493
+ .value=${((_18 = this.project.structuralSafetyRate) === null || _18 === void 0 ? void 0 : _18.toString()) || ''}
494
+ @input=${this._onInputChange}
495
+ suffix-text="%"
496
+ ></md-outlined-text-field>
497
+ </span>
498
+ </div>
499
+ </div>
500
+
501
+ <div row separate-container>
502
+ <div>
503
+ <span>로봇 작업 대수</span>
504
+ <span
505
+ ><md-outlined-text-field
506
+ type="text"
507
+ numeric
508
+ project
509
+ name="robotCount"
510
+ .value=${((_19 = this.project.robotCount) === null || _19 === void 0 ? void 0 : _19.toString()) || ''}
511
+ @input=${this._onInputChange}
512
+ ></md-outlined-text-field>
513
+ </span>
514
+ </div>
515
+ </div>
516
+
517
+ <hr />
518
+
519
+ <div row separate-container>
520
+ <div>
521
+ <span>설계 변경 횟수</span>
522
+ <span
523
+ ><md-outlined-text-field
524
+ type="text"
525
+ numeric
526
+ building-complex
527
+ name="designChangeCount"
528
+ .value=${((_21 = (_20 = this.project.buildingComplex) === null || _20 === void 0 ? void 0 : _20.designChangeCount) === null || _21 === void 0 ? void 0 : _21.toString()) || ''}
529
+ @input=${this._onInputChange}
530
+ ></md-outlined-text-field>
531
+ </span>
532
+ </div>
533
+ <div>
534
+ <span>계획 공정률</span>
535
+ <span
536
+ ><md-outlined-text-field
537
+ type="text"
538
+ numeric
539
+ building-complex
540
+ name="plannedProgress"
541
+ .value=${((_23 = (_22 = this.project.buildingComplex) === null || _22 === void 0 ? void 0 : _22.plannedProgress) === null || _23 === void 0 ? void 0 : _23.toString()) || ''}
542
+ @input=${this._onInputChange}
543
+ suffix-text="%"
544
+ ></md-outlined-text-field>
545
+ </span>
546
+ </div>
547
+ </div>
548
+
549
+ <div row separate-container>
550
+ <div>
551
+ <span>실제 공정률</span>
552
+ <span
553
+ ><md-outlined-text-field
554
+ type="text"
555
+ numeric
556
+ building-complex
557
+ name="actualProgress"
558
+ .value=${((_25 = (_24 = this.project.buildingComplex) === null || _24 === void 0 ? void 0 : _24.actualProgress) === null || _25 === void 0 ? void 0 : _25.toString()) || ''}
559
+ @input=${this._onInputChange}
560
+ suffix-text="%"
561
+ ></md-outlined-text-field>
562
+ </span>
563
+ </div>
564
+ </div>
565
+ </div>
566
+
567
+ </div>
568
+ </div>
569
+ `;
570
+ }
571
+ async pageInitialized(lifecycle) { }
572
+ async pageUpdated(changes, lifecycle) {
573
+ if (!this.active)
574
+ return;
575
+ this.projectId = await this._resolveProjectId(lifecycle.resourceId || '');
576
+ await this.initProject(this.projectId);
577
+ }
578
+ /**
579
+ * resourceId 를 정상 Project.id (UUID) 로 변환한다.
580
+ * - UUID 형식이면 그대로 사용
581
+ * - 그 외 (code 또는 비어있음) 에는 현재 테넌트 컨텍스트의 프로젝트 조회
582
+ */
583
+ async _resolveProjectId(resourceId) {
584
+ var _a, _b;
585
+ if (resourceId && resourceId.length === 36)
586
+ return resourceId;
587
+ const response = await client.query({
588
+ query: gql `
589
+ query CurrentProject {
590
+ currentProject {
591
+ id
592
+ }
593
+ }
594
+ `
595
+ });
596
+ return ((_b = (_a = response.data) === null || _a === void 0 ? void 0 : _a.currentProject) === null || _b === void 0 ? void 0 : _b.id) || '';
597
+ }
598
+ async initProject(projectId = '') {
599
+ var _a, _b, _c, _d;
600
+ const response = await client.query({
601
+ query: gql `
602
+ query Project($id: String!, $filters: [Filter!]) {
603
+ project(id: $id) {
604
+ id
605
+ code
606
+ state
607
+ tenantDomain {
608
+ id
609
+ subdomain
610
+ name
611
+ }
612
+ name
613
+ documentNaming
614
+ startDate
615
+ endDate
616
+ mainPhoto {
617
+ fullpath
618
+ }
619
+ totalProgress
620
+ weeklyProgress
621
+ kpi
622
+ inspPassRate
623
+ robotProgressRate
624
+ structuralSafetyRate
625
+ robotCount
626
+ buildingComplex {
627
+ id
628
+ address
629
+ siteType
630
+ latitude
631
+ longitude
632
+ area
633
+ coverageRatio
634
+ floorAreaRatio
635
+ structureType
636
+ workerCount
637
+ designChangeCount
638
+ plannedProgress
639
+ actualProgress
640
+ clientCompany
641
+ constructionCompany
642
+ supervisoryCompany
643
+ designCompany
644
+ drawing {
645
+ id
646
+ name
647
+ }
648
+ constructionType
649
+ constructionCost
650
+ etc
651
+ notice
652
+ householdCount
653
+ buildingCount
654
+ overallConstructorEmails
655
+ taskConstructorEmails
656
+ overallSupervisoryEmails
657
+ taskSupervisoryEmails
658
+ virtualTourLink
659
+ buildings {
660
+ id
661
+ name
662
+ floorCount
663
+ hasBasement
664
+ basementFloorCount
665
+ }
666
+ }
667
+ }
668
+
669
+ employees(filters: $filters) {
670
+ items {
671
+ id
672
+ name
673
+ jobResponsibility
674
+ active
675
+ user {
676
+ id
677
+ name
678
+ email
679
+ }
680
+ }
681
+ }
682
+ }
683
+ `,
684
+ variables: {
685
+ id: projectId,
686
+ filters: [
687
+ {
688
+ name: 'active',
689
+ operator: 'eq',
690
+ value: true
691
+ },
692
+ {
693
+ name: 'userId',
694
+ operator: 'is_not_null',
695
+ value: ''
696
+ }
697
+ ]
698
+ },
699
+ context: { headers: tenantHeaders((_a = this.project) === null || _a === void 0 ? void 0 : _a.code) }
700
+ });
701
+ this.project = ((_b = response.data) === null || _b === void 0 ? void 0 : _b.project) || Object.assign({}, this.defaultProject);
702
+ const items = ((_d = (_c = response.data) === null || _c === void 0 ? void 0 : _c.employees) === null || _d === void 0 ? void 0 : _d.items) || [];
703
+ this.overallConstructorList = this._filterUserByPermission(items, 'OVERALL_CONSTRUCTOR');
704
+ this.taskConstructorList = this._filterUserByPermission(items, 'TASK_CONSTRUCTOR');
705
+ this.overallSupervisoryList = this._filterUserByPermission(items, 'OVERALL_SUPERVISORY');
706
+ this.taskSupervisoryList = this._filterUserByPermission(items, 'TASK_SUPERVISORY');
707
+ this.updateContext();
708
+ }
709
+ _filterUserByPermission(userList, permission) {
710
+ return userList
711
+ .filter(v => v.jobResponsibility == permission || v.jobResponsibility == 'ADMIN')
712
+ .map(v => ({ name: v.name, value: v.user.email }));
713
+ }
714
+ _stripTypename(obj) {
715
+ if (Array.isArray(obj)) {
716
+ return obj.map(item => this._stripTypename(item));
717
+ }
718
+ if (obj && typeof obj === 'object' && !(obj instanceof File)) {
719
+ const cleaned = {};
720
+ for (const [key, value] of Object.entries(obj)) {
721
+ if (key === '__typename')
722
+ continue;
723
+ cleaned[key] = this._stripTypename(value);
724
+ }
725
+ return cleaned;
726
+ }
727
+ return obj;
728
+ }
729
+ async _saveProject() {
730
+ // 첨부 파일 필드 제거 (첨부 파일은 {filename}Upload 로 전송)
731
+ delete this.project.mainPhoto;
732
+ delete this.project.buildingComplex.drawing;
733
+ // ProjectPatch 입력 타입에 없는 표시 전용 필드 제거 (테넌트 승격 UI 표시용)
734
+ const projectCode = this.project.code; // 도메인 헤더 용으로 미리 보존
735
+ delete this.project.code;
736
+ delete this.project.tenantDomain;
737
+ // state 는 raw 값('10'/'20')으로 로드되지만 ProjectPatch 는 enum 키 기대 — 일반
738
+ // save 에서는 state 변경 의도 없으므로 patch 에서 제외. (state 변경은 _onUnfinalize 별도)
739
+ delete this.project.state;
740
+ const response = await client.mutate({
741
+ mutation: gql `
742
+ mutation UpdateProject($project: ProjectPatch!) {
743
+ updateProject(project: $project) {
744
+ id
745
+ }
746
+ }
747
+ `,
748
+ variables: {
749
+ project: this._stripTypename(this.project)
750
+ },
751
+ context: {
752
+ hasUpload: true,
753
+ headers: tenantHeaders(projectCode)
754
+ }
755
+ });
756
+ if (!response.errors) {
757
+ notify({ message: '저장에 성공하였습니다.' });
758
+ }
759
+ }
760
+ /**
761
+ * 주소 옆 "정보 연동" 버튼 handler.
762
+ * - 현재 폼의 주소를 backend 로 보냄 → backend 가 EnvVar 갱신 + 세움터 시나리오 호출
763
+ * - 결과 (한글 raw + 숫자) 를 받아 BuildingComplex 폼 필드들에 매핑
764
+ * · siteType (한글) → SiteType enum 변환 (BC.siteType, enum 컬럼)
765
+ * · siteType (한글) → BC.constructionType 그대로 (자유 텍스트 컬럼)
766
+ * · area / floorAreaRatio / coverageRatio / householdCount / buildingCount /
767
+ * structureType → BC 동명 컬럼에 직접 대입
768
+ * · upperFloorCount / lowerFloorCount / bldNm / useAprDay / pmsDay → BC 컬럼
769
+ * 없음, 매핑 skip (참고용 콘솔 로그만)
770
+ */
771
+ async _collectBasicInfo() {
772
+ var _a, _b, _c, _d;
773
+ const address = (((_b = (_a = this.project) === null || _a === void 0 ? void 0 : _a.buildingComplex) === null || _b === void 0 ? void 0 : _b.address) || '').trim();
774
+ if (!address) {
775
+ notify({ message: '먼저 프로젝트 주소를 입력해 주세요.' });
776
+ return;
777
+ }
778
+ this.collectingBasicInfo = true;
779
+ try {
780
+ const response = await client.mutate({
781
+ mutation: gql `
782
+ mutation CollectProjectBasicInfo($projectId: String!, $address: String) {
783
+ collectProjectBasicInfo(projectId: $projectId, address: $address) {
784
+ source
785
+ label
786
+ ok
787
+ message
788
+ data
789
+ }
790
+ }
791
+ `,
792
+ variables: { projectId: this.project.id, address },
793
+ context: { headers: tenantHeaders((_c = this.project) === null || _c === void 0 ? void 0 : _c.code) }
794
+ });
795
+ if (response.errors) {
796
+ throw new Error(response.errors.map((e) => e.message).join('\n'));
797
+ }
798
+ const result = (_d = response.data) === null || _d === void 0 ? void 0 : _d.collectProjectBasicInfo;
799
+ if (!(result === null || result === void 0 ? void 0 : result.ok)) {
800
+ notify({ message: (result === null || result === void 0 ? void 0 : result.message) || '업데이트할 정보가 없습니다.' });
801
+ return;
802
+ }
803
+ const data = result.data || {};
804
+ this._applyBasicInfoToForm(data);
805
+ this.requestUpdate();
806
+ notify({ message: '외부 시스템에서 기본 정보를 가져왔습니다. 검토 후 [저장]을 눌러주세요.' });
807
+ }
808
+ catch (err) {
809
+ console.warn('[정보 연동] 실패', err);
810
+ notify({ message: '외부 시스템 연동이 일시적으로 안 됩니다.' });
811
+ }
812
+ finally {
813
+ this.collectingBasicInfo = false;
814
+ }
815
+ }
816
+ /**
817
+ * 한글 주용도명 → SiteType enum.
818
+ * - NFC normalize + 공백 제거로 보이지 않는 문자/표기 차이 대응
819
+ * - 매핑 미존재시 null
820
+ */
821
+ _siteTypeFromKorean(korean) {
822
+ if (!korean)
823
+ return null;
824
+ const KEYS = {
825
+ 공동주택: 'APARTMENT_COMPLEX',
826
+ 단독주택: 'DETACHED_HOUSE',
827
+ 제1종근린생활시설: 'NEIGHBORHOOD_FACILITY',
828
+ 제2종근린생활시설: 'NEIGHBORHOOD_FACILITY',
829
+ 근린생활시설: 'NEIGHBORHOOD_FACILITY',
830
+ 업무시설: 'OFFICE',
831
+ 판매시설: 'COMMERCIAL',
832
+ 교육연구시설: 'EDUCATION',
833
+ 의료시설: 'MEDICAL',
834
+ 문화및집회시설: 'CULTURAL',
835
+ 종교시설: 'RELIGIOUS',
836
+ 공장: 'INDUSTRIAL',
837
+ 창고시설: 'INDUSTRIAL',
838
+ 운수시설: 'TRANSPORT'
839
+ };
840
+ const key = korean.normalize('NFC').replace(/\s+/g, '').trim();
841
+ return KEYS[key] || null;
842
+ }
843
+ /**
844
+ * 시나리오 result data 를 폼에 매핑 — BuildingComplex 와 Project 양쪽.
845
+ * 빈 값(null/undefined/'')은 기존 값 유지 — 자동 수집이 사용자 입력을 무리하게 덮지 않도록.
846
+ */
847
+ _applyBasicInfoToForm(data) {
848
+ const bc = Object.assign({}, this.project.buildingComplex);
849
+ const setBC = (key, value) => {
850
+ if (value === null || value === undefined || value === '')
851
+ return;
852
+ bc[key] = value;
853
+ };
854
+ setBC('area', this._toNum(data.area));
855
+ setBC('floorAreaRatio', this._toNum(data.floorAreaRatio));
856
+ setBC('coverageRatio', this._toNum(data.coverageRatio));
857
+ setBC('householdCount', this._toNum(data.householdCount));
858
+ setBC('buildingCount', this._toNum(data.buildingCount));
859
+ setBC('structureType', data.structureType);
860
+ // siteType: 한글 → SiteType enum 매핑. 매핑 안 되면 BC.siteType 빈 채로.
861
+ if (data.siteType) {
862
+ const enumVal = this._siteTypeFromKorean(data.siteType);
863
+ if (enumVal)
864
+ bc.siteType = enumVal;
865
+ }
866
+ // Project 엔티티 컬럼 — 착공일/준공일.
867
+ // 세움터 useAprDay (사용승인일) = 준공으로 매핑. stcnsDay = 착공.
868
+ const project = Object.assign(Object.assign({}, this.project), { buildingComplex: bc });
869
+ if (data.startDate)
870
+ project.startDate = data.startDate;
871
+ if (data.endDate)
872
+ project.endDate = data.endDate;
873
+ this.project = project;
874
+ // 참고 메타 — BC/Project 직접 컬럼 없음, 콘솔로만 (permitDate=건축허가일, bldNm 등)
875
+ console.log('[정보 연동] meta:', JSON.stringify({
876
+ upperFloorCount: data.upperFloorCount,
877
+ lowerFloorCount: data.lowerFloorCount,
878
+ bldNm: data.bldNm,
879
+ permitDate: data.permitDate
880
+ }));
881
+ }
882
+ _toNum(v) {
883
+ if (v === null || v === undefined || v === '')
884
+ return null;
885
+ const n = Number(v);
886
+ return Number.isFinite(n) ? n : null;
887
+ }
888
+ async _promoteTenant() {
889
+ if (!this.project.id)
890
+ return;
891
+ if (!confirm('이 프로젝트를 테넌트로 승격하시겠습니까?\n승격 시 관리번호(YYYY-NNNNN)가 자동 발번되며 변경할 수 없습니다.')) {
892
+ return;
893
+ }
894
+ const response = await client.mutate({
895
+ mutation: gql `
896
+ mutation PromoteProjectToTenant($projectId: String!) {
897
+ promoteProjectToTenant(projectId: $projectId) {
898
+ id
899
+ code
900
+ }
901
+ }
902
+ `,
903
+ variables: { projectId: this.project.id }
904
+ });
905
+ if (!response.errors) {
906
+ notify({ message: `테넌트로 승격되었습니다. 관리번호: ${response.data.promoteProjectToTenant.code}` });
907
+ await this.initProject(this.projectId);
908
+ }
909
+ }
910
+ async _demoteTenant() {
911
+ if (!this.project.id)
912
+ return;
913
+ if (!confirm('이 프로젝트의 테넌트를 강등하시겠습니까?\n관리번호는 보존되며 재승격 시 같은 번호가 재사용됩니다.')) {
914
+ return;
915
+ }
916
+ const response = await client.mutate({
917
+ mutation: gql `
918
+ mutation DemoteProjectTenant($projectId: String!) {
919
+ demoteProjectTenant(projectId: $projectId)
920
+ }
921
+ `,
922
+ variables: { projectId: this.project.id }
923
+ });
924
+ if (!response.errors) {
925
+ notify({ message: '테넌트가 강등되었습니다.' });
926
+ await this.initProject(this.projectId);
927
+ }
928
+ }
929
+ // 동 적용 버튼을 누르면 입력한 수 만큼 해당 단지에 동 데이터 생성
930
+ _setBuilding() {
931
+ var _a, _b, _c, _d, _e;
932
+ const buildingCount = ((_b = (_a = this.project) === null || _a === void 0 ? void 0 : _a.buildingComplex) === null || _b === void 0 ? void 0 : _b.buildingCount) || 0;
933
+ const buildingInitData = { name: undefined, floorCount: undefined };
934
+ // 빌딩 데이터가 없으면 빈 배열 넣어줌
935
+ if (!((_e = (_d = (_c = this.project) === null || _c === void 0 ? void 0 : _c.buildingComplex) === null || _d === void 0 ? void 0 : _d.buildings) === null || _e === void 0 ? void 0 : _e.length)) {
936
+ this.project.buildingComplex.buildings = [];
937
+ }
938
+ if (this.project.buildingComplex.buildings.length >= buildingCount) {
939
+ // 동 수가 더 작게 들어오면 기존 배열을 필요한 크기만큼 잘라내기
940
+ this.project.buildingComplex.buildings = [...this.project.buildingComplex.buildings.slice(0, buildingCount)];
941
+ }
942
+ else {
943
+ // 동수가 더 크게 들어오면 기존 배열 + 빈 값을 채움
944
+ const additionalCount = buildingCount - this.project.buildingComplex.buildings.length;
945
+ const additionalBuildings = Array.from({ length: additionalCount }, () => (Object.assign({}, buildingInitData)));
946
+ this.project.buildingComplex.buildings = [...this.project.buildingComplex.buildings, ...additionalBuildings];
947
+ }
948
+ // 리렌더링
949
+ this.project = Object.assign({}, this.project);
950
+ }
951
+ // Input 요소의 값이 변경될 때 호출되는 콜백 함수
952
+ _onInputChange(event, idx) {
953
+ const target = event.target;
954
+ let inputVal = target.value;
955
+ // 숫자 타입은 다른 문자 입력 제거
956
+ if (target.hasAttribute('numeric')) {
957
+ inputVal = Number(inputVal.replace(/[^\d.]/g, ''));
958
+ }
959
+ if (target.hasAttribute('project')) {
960
+ this.project[target.name] = inputVal;
961
+ // documentNaming 필드가 변경되면 실시간으로 미리보기 업데이트
962
+ if (target.name === 'documentNaming') {
963
+ this.requestUpdate();
964
+ }
965
+ }
966
+ else if (target.hasAttribute('building-complex')) {
967
+ this.project.buildingComplex[target.name] = inputVal;
968
+ }
969
+ else if (target.hasAttribute('building')) {
970
+ this.project.buildingComplex.buildings[idx][target.name] = inputVal;
971
+ }
972
+ }
973
+ // 이미지 업로드
974
+ async onCreateAttachment(e) {
975
+ const target = e.target;
976
+ const file = (target.name === 'mainPhoto' ? e.detail : e.detail[0]) || null;
977
+ if (target.name === 'mainPhoto') {
978
+ this.project.mainPhotoUpload = file;
979
+ }
980
+ else {
981
+ this.project.buildingComplex.drawingUpload = file;
982
+ }
983
+ }
984
+ _handleSelectionChange(e) {
985
+ const name = e.target.getAttribute('name');
986
+ const selectedValues = e.detail.selectedValues;
987
+ this.project.buildingComplex[name] = selectedValues;
988
+ }
989
+ _toggleBasement(idx) {
990
+ const building = this.project.buildingComplex.buildings[idx];
991
+ building.hasBasement = !building.hasBasement;
992
+ // 층 수가 0이거나 체크 해제시 지하층수 초기화 (플레이스 홀더 나오게 하기 위함)
993
+ if (building.basementFloorCount == 0 || !building.hasBasement) {
994
+ building.basementFloorCount = undefined;
995
+ }
996
+ this.project.buildingComplex.buildings[idx]['hasBasement'] = building.hasBasement;
997
+ this.requestUpdate();
998
+ }
999
+ };
1000
+ SvProjectUpdate.styles = [
1001
+ css `
1002
+ :host {
1003
+ display: grid;
1004
+ grid-template-rows: 55px auto;
1005
+ color: #4e5055;
1006
+
1007
+ width: 100%;
1008
+ background-color: var(--md-sys-color-background, #f6f6f6);
1009
+ overflow-y: auto;
1010
+
1011
+ --grid-record-emphasized-background-color: red;
1012
+ --grid-record-emphasized-color: yellow;
1013
+ }
1014
+
1015
+ md-outlined-text-field {
1016
+ width: 100%;
1017
+
1018
+ --md-outlined-text-field-container-shape: 5px;
1019
+ --md-outlined-text-field-outline-color: rgba(51, 51, 51, 0.2);
1020
+ --md-outlined-text-field-focus-outline-color: #1f7fd9;
1021
+ --md-outlined-text-field-focus-outline-width: 1px;
1022
+ --md-sys-color-primary: #586878;
1023
+ --md-outlined-text-field-input-text-size: 14px;
1024
+ --md-outlined-field-bottom-space: 3px;
1025
+ --md-outlined-field-top-space: 3px;
1026
+ --md-outlined-field-leading-space: var(--spacing-medium, 8px);
1027
+ --md-outlined-field-trailing-space: var(--spacing-medium, 8px);
1028
+ }
1029
+ select2-component {
1030
+ width: 100%;
1031
+ }
1032
+ md-filled-select {
1033
+ width: 100%;
1034
+ --md-filled-select-text-field-container-color: transparent;
1035
+ --md-filled-select-text-field-active-indicator-color: #999;
1036
+ --md-filled-select-text-field-input-text-size: 14px;
1037
+ --md-filled-select-text-field-input-text-line-height: 6px;
1038
+ }
1039
+ input[type='date'] {
1040
+ border: 1px solid rgba(51, 51, 51, 0.2);
1041
+ padding: var(--spacing-small, 4px) var(--spacing-medium, 8px);
1042
+ border-radius: 5px;
1043
+ }
1044
+ md-outlined-text-field[type='textarea'] {
1045
+ width: 100%;
1046
+ height: 120px;
1047
+ resize: none;
1048
+ }
1049
+
1050
+ ox-input-image {
1051
+ width: 120px;
1052
+ height: 90px;
1053
+ }
1054
+ ox-input-file {
1055
+ width: 120px;
1056
+ height: 90px;
1057
+ padding: 0;
1058
+ }
1059
+
1060
+ div[body] {
1061
+ display: flex;
1062
+ margin: var(--spacing-large, 12px);
1063
+ margin-top: 0;
1064
+ gap: var(--spacing-medium, 8px);
1065
+
1066
+ & > div {
1067
+ display: flex;
1068
+ height: fit-content;
1069
+ flex-direction: column;
1070
+ padding: var(--spacing-large, 12px);
1071
+ background-color: var(--md-sys-color-on-primary);
1072
+ border: 1px solid #cccccc80;
1073
+ border-radius: 5px;
1074
+ gap: var(--spacing-medium, 8px);
1075
+
1076
+ h3 {
1077
+ color: #2e79be;
1078
+ font-size: 16px;
1079
+ margin: 0px;
1080
+ }
1081
+
1082
+ div[row] {
1083
+ display: flex;
1084
+ align-items: center;
1085
+
1086
+ span:first-child {
1087
+ flex: 0.3;
1088
+ text-align: right;
1089
+ font-size: 14px;
1090
+ }
1091
+
1092
+ span:last-child {
1093
+ flex: 0.7;
1094
+ display: flex;
1095
+ gap: var(--spacing-medium, 8px);
1096
+ margin-left: var(--spacing-medium, 8px);
1097
+ align-items: center;
1098
+
1099
+ &.address-row {
1100
+ md-outlined-text-field {
1101
+ flex: 1;
1102
+ }
1103
+ .info-link-btn {
1104
+ flex: 0 0 auto;
1105
+ --md-elevated-button-container-color: #2e79be;
1106
+ --md-elevated-button-label-text-color: #fff;
1107
+ --md-elevated-button-container-height: 36px;
1108
+ }
1109
+ }
1110
+
1111
+ &[align-end] {
1112
+ align-items: end;
1113
+ }
1114
+ }
1115
+ }
1116
+ }
1117
+
1118
+ div[project-info] {
1119
+ flex: 0.55;
1120
+ }
1121
+
1122
+ div[document-preview] {
1123
+ width: 100%;
1124
+ font-size: 12px;
1125
+ color: #2e79be;
1126
+ font-weight: 500;
1127
+ background-color: #f8f9fa;
1128
+ padding: 8px 12px;
1129
+ border-radius: 4px;
1130
+ border-left: 3px solid #2e79be;
1131
+ margin-top: 4px;
1132
+ }
1133
+
1134
+ div[detail-info] {
1135
+ flex: 0.45;
1136
+ padding: 0px;
1137
+ gap: 10px;
1138
+ background-color: transparent;
1139
+ border: none;
1140
+
1141
+ & > div {
1142
+ display: flex;
1143
+ flex-direction: column;
1144
+ gap: var(--spacing-medium, 8px);
1145
+ border: 1px solid #cccccc80;
1146
+ border-radius: 5px;
1147
+ padding: var(--spacing-large, 12px);
1148
+ background-color: var(--md-sys-color-on-primary);
1149
+
1150
+ md-outlined-text-field[type='text'] {
1151
+ width: 60%;
1152
+ }
1153
+
1154
+ md-elevated-button {
1155
+ --md-elevated-button-container-height: 30px;
1156
+ --md-elevated-button-container-color: var(--md-sys-color-on-primary);
1157
+ --md-elevated-button-label-text-size: 16px;
1158
+ }
1159
+ hr {
1160
+ border: 1px rgba(51, 51, 51, 0.1) dashed;
1161
+ width: 100%;
1162
+ margin-bottom: var(--spacing-tiny, 2px);
1163
+ }
1164
+ div[warn] {
1165
+ font-size: 12px;
1166
+ color: red;
1167
+ margin-left: var(--spacing-small, 4px);
1168
+ margin-bottom: var(--spacing-small, 4px);
1169
+ }
1170
+
1171
+ md-outlined-button {
1172
+ min-height: 30px;
1173
+ padding: 0 var(--spacing-large, 12px);
1174
+ box-shadow: 1px 1px 1px #0000001a;
1175
+ --md-outlined-button-label-text-color: #586878;
1176
+ --md-outlined-button-label-text-weight: bold;
1177
+ --md-sys-color-outline: rgba(51, 51, 51, 0.2);
1178
+ }
1179
+
1180
+ div[row] {
1181
+ span:first-child {
1182
+ flex: 0.2;
1183
+ }
1184
+ span:last-child {
1185
+ flex: 0.8;
1186
+ }
1187
+ }
1188
+
1189
+ div[separate-container] {
1190
+ display: grid;
1191
+ grid-template-columns: repeat(2, 1fr);
1192
+ gap: 8px 30px;
1193
+ margin-left: 15px;
1194
+
1195
+ md-outlined-text-field {
1196
+ width: 80px;
1197
+ }
1198
+
1199
+ & > div {
1200
+ display: flex;
1201
+ gap: 15px;
1202
+
1203
+ span[floor-row] {
1204
+ display: inline-flex;
1205
+ align-items: center;
1206
+ margin-left: 5px;
1207
+ }
1208
+
1209
+ span[floor-title] {
1210
+ min-width: 33px;
1211
+ margin-right: var(--spacing-small, 4px);
1212
+ }
1213
+ span:first-child {
1214
+ flex: 0.4;
1215
+ justify-content: flex-start;
1216
+ display: flex;
1217
+ }
1218
+ span:last-child {
1219
+ flex: 0.6;
1220
+ display: flex;
1221
+ align-items: center;
1222
+ padding-left: var(--spacing-medium, 8px);
1223
+ }
1224
+ }
1225
+
1226
+ div[basement-container] {
1227
+ display: flex;
1228
+ align-items: center;
1229
+ gap: 4px;
1230
+
1231
+ div[basement-checkbox] {
1232
+ display: flex;
1233
+ align-items: center;
1234
+ cursor: pointer;
1235
+
1236
+ md-icon {
1237
+ --md-icon-size: 25px;
1238
+ color: #808080;
1239
+ transition: opacity 0.2s ease;
1240
+ }
1241
+
1242
+ &[disabled] {
1243
+ cursor: not-allowed;
1244
+ opacity: 0.4;
1245
+
1246
+ md-icon {
1247
+ color: #cccccc;
1248
+ }
1249
+ }
1250
+
1251
+ &[checked] md-icon {
1252
+ background-color: #2e79be;
1253
+ color: white;
1254
+ border-radius: 3px;
1255
+ }
1256
+ }
1257
+
1258
+ md-outlined-text-field[basement-floor] {
1259
+ width: 70px;
1260
+ margin-left: var(--spacing-small, 4px);
1261
+ opacity: 1;
1262
+ transition: opacity 0.2s ease;
1263
+
1264
+ &[disabled] {
1265
+ opacity: 0.4;
1266
+ pointer-events: none;
1267
+ }
1268
+ }
1269
+ }
1270
+ }
1271
+
1272
+ &[project] {
1273
+ div[separate-container] {
1274
+ margin-left: 15px;
1275
+
1276
+ & > div {
1277
+ align-items: center;
1278
+
1279
+ span:first-child {
1280
+ flex: 0.4;
1281
+ min-width: 100px;
1282
+ }
1283
+ span:last-child {
1284
+ flex: 0.4;
1285
+ margin-left: 0;
1286
+ }
1287
+ }
1288
+ }
1289
+ }
1290
+ }
1291
+ }
1292
+ }
1293
+ `
1294
+ ];
1295
+ __decorate([
1296
+ state(),
1297
+ __metadata("design:type", String)
1298
+ ], SvProjectUpdate.prototype, "projectId", void 0);
1299
+ __decorate([
1300
+ state(),
1301
+ __metadata("design:type", Object)
1302
+ ], SvProjectUpdate.prototype, "project", void 0);
1303
+ __decorate([
1304
+ state(),
1305
+ __metadata("design:type", Array)
1306
+ ], SvProjectUpdate.prototype, "selectedValues", void 0);
1307
+ __decorate([
1308
+ state(),
1309
+ __metadata("design:type", Array)
1310
+ ], SvProjectUpdate.prototype, "overallConstructorList", void 0);
1311
+ __decorate([
1312
+ state(),
1313
+ __metadata("design:type", Array)
1314
+ ], SvProjectUpdate.prototype, "taskConstructorList", void 0);
1315
+ __decorate([
1316
+ state(),
1317
+ __metadata("design:type", Array)
1318
+ ], SvProjectUpdate.prototype, "taskSupervisoryList", void 0);
1319
+ __decorate([
1320
+ state(),
1321
+ __metadata("design:type", Array)
1322
+ ], SvProjectUpdate.prototype, "overallSupervisoryList", void 0);
1323
+ __decorate([
1324
+ state(),
1325
+ __metadata("design:type", Boolean)
1326
+ ], SvProjectUpdate.prototype, "collectingBasicInfo", void 0);
1327
+ SvProjectUpdate = __decorate([
1328
+ customElement('sv-project-update')
1329
+ ], SvProjectUpdate);
1330
+ export { SvProjectUpdate };
1331
+ //# sourceMappingURL=sv-project-update.js.map