@dssp/dkpi 1.0.0-alpha.9 → 1.0.0-alpha.90

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