@dssp/dcsp 1.0.2 → 1.0.6

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 (161) hide show
  1. package/dist-client/bootstrap.js +13 -10
  2. package/dist-client/bootstrap.js.map +1 -1
  3. package/dist-client/components/kpi-2d-lookup-chart.d.ts +50 -0
  4. package/dist-client/components/kpi-2d-lookup-chart.js +263 -0
  5. package/dist-client/components/kpi-2d-lookup-chart.js.map +1 -0
  6. package/dist-client/components/kpi-boxplot-chart.d.ts +31 -0
  7. package/dist-client/components/kpi-boxplot-chart.js +306 -0
  8. package/dist-client/components/kpi-boxplot-chart.js.map +1 -0
  9. package/dist-client/components/kpi-lookup-chart.d.ts +58 -0
  10. package/dist-client/components/kpi-lookup-chart.js +464 -0
  11. package/dist-client/components/kpi-lookup-chart.js.map +1 -0
  12. package/dist-client/components/kpi-mini-trend-chart.d.ts +14 -0
  13. package/dist-client/components/kpi-mini-trend-chart.js +180 -0
  14. package/dist-client/components/kpi-mini-trend-chart.js.map +1 -0
  15. package/dist-client/components/kpi-radar-chart.d.ts +17 -0
  16. package/dist-client/components/kpi-radar-chart.js +259 -0
  17. package/dist-client/components/kpi-radar-chart.js.map +1 -0
  18. package/dist-client/components/kpi-single-boxplot-chart.d.ts +36 -0
  19. package/dist-client/components/kpi-single-boxplot-chart.js +417 -0
  20. package/dist-client/components/kpi-single-boxplot-chart.js.map +1 -0
  21. package/dist-client/components/kpi-step-lookup-chart.d.ts +33 -0
  22. package/dist-client/components/kpi-step-lookup-chart.js +181 -0
  23. package/dist-client/components/kpi-step-lookup-chart.js.map +1 -0
  24. package/dist-client/components/kpi-trend-chart.d.ts +25 -0
  25. package/dist-client/components/kpi-trend-chart.js +241 -0
  26. package/dist-client/components/kpi-trend-chart.js.map +1 -0
  27. package/dist-client/google-map/common-google-map.d.ts +35 -0
  28. package/dist-client/google-map/common-google-map.js +349 -0
  29. package/dist-client/google-map/common-google-map.js.map +1 -0
  30. package/dist-client/google-map/google-map-loader.d.ts +6 -0
  31. package/dist-client/google-map/google-map-loader.js +23 -0
  32. package/dist-client/google-map/google-map-loader.js.map +1 -0
  33. package/dist-client/pages/kpi-admin/dssp-kpi-overview.d.ts +47 -0
  34. package/dist-client/pages/kpi-admin/dssp-kpi-overview.js +393 -0
  35. package/dist-client/pages/kpi-admin/dssp-kpi-overview.js.map +1 -0
  36. package/dist-client/pages/kpi-admin/kpi-system-guide.d.ts +18 -0
  37. package/dist-client/pages/kpi-admin/kpi-system-guide.js +696 -0
  38. package/dist-client/pages/kpi-admin/kpi-system-guide.js.map +1 -0
  39. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.d.ts +46 -0
  40. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js +945 -0
  41. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js.map +1 -0
  42. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.d.ts +34 -0
  43. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js +238 -0
  44. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js.map +1 -0
  45. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.d.ts +37 -0
  46. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js +689 -0
  47. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js.map +1 -0
  48. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.d.ts +57 -0
  49. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js +866 -0
  50. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js.map +1 -0
  51. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.d.ts +69 -0
  52. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js +790 -0
  53. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js.map +1 -0
  54. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.d.ts +22 -0
  55. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js +344 -0
  56. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js.map +1 -0
  57. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.d.ts +21 -0
  58. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.js +335 -0
  59. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.js.map +1 -0
  60. package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.d.ts +51 -0
  61. package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.js +693 -0
  62. package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.js.map +1 -0
  63. package/dist-client/pages/sv-project-complete.d.ts +31 -0
  64. package/dist-client/pages/sv-project-complete.js +364 -0
  65. package/dist-client/pages/sv-project-complete.js.map +1 -0
  66. package/dist-client/pages/sv-project-detail.d.ts +73 -0
  67. package/dist-client/pages/sv-project-detail.js +1387 -0
  68. package/dist-client/pages/sv-project-detail.js.map +1 -0
  69. package/dist-client/route.d.ts +1 -1
  70. package/dist-client/route.js +15 -0
  71. package/dist-client/route.js.map +1 -1
  72. package/dist-client/shared/complete-api.d.ts +45 -0
  73. package/dist-client/shared/complete-api.js +274 -0
  74. package/dist-client/shared/complete-api.js.map +1 -0
  75. package/dist-client/shared/func.d.ts +2 -0
  76. package/dist-client/shared/func.js +22 -0
  77. package/dist-client/shared/func.js.map +1 -0
  78. package/dist-client/shared/integration-fetch.d.ts +35 -0
  79. package/dist-client/shared/integration-fetch.js +53 -0
  80. package/dist-client/shared/integration-fetch.js.map +1 -0
  81. package/dist-client/shared/kpi-project-sync.d.ts +16 -0
  82. package/dist-client/shared/kpi-project-sync.js +129 -0
  83. package/dist-client/shared/kpi-project-sync.js.map +1 -0
  84. package/dist-client/tsconfig.tsbuildinfo +1 -1
  85. package/dist-client/viewparts/menu-tools.d.ts +7 -0
  86. package/dist-client/viewparts/menu-tools.js +168 -21
  87. package/dist-client/viewparts/menu-tools.js.map +1 -1
  88. package/dist-server/index.d.ts +2 -0
  89. package/dist-server/index.js +5 -0
  90. package/dist-server/index.js.map +1 -1
  91. package/dist-server/middlewares/index.d.ts +7 -0
  92. package/dist-server/middlewares/index.js +100 -0
  93. package/dist-server/middlewares/index.js.map +1 -0
  94. package/dist-server/service/index.d.ts +4 -0
  95. package/dist-server/service/index.js +19 -0
  96. package/dist-server/service/index.js.map +1 -0
  97. package/dist-server/service/kpi-metric-value/index.d.ts +4 -0
  98. package/dist-server/service/kpi-metric-value/index.js +8 -0
  99. package/dist-server/service/kpi-metric-value/index.js.map +1 -0
  100. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.d.ts +198 -0
  101. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js +1067 -0
  102. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js.map +1 -0
  103. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.d.ts +15 -0
  104. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js +72 -0
  105. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js.map +1 -0
  106. package/dist-server/service/kpi-stat/index.d.ts +4 -0
  107. package/dist-server/service/kpi-stat/index.js +8 -0
  108. package/dist-server/service/kpi-stat/index.js.map +1 -0
  109. package/dist-server/service/kpi-stat/kpi-stat-query.d.ts +13 -0
  110. package/dist-server/service/kpi-stat/kpi-stat-query.js +751 -0
  111. package/dist-server/service/kpi-stat/kpi-stat-query.js.map +1 -0
  112. package/dist-server/service/kpi-stat/kpi-stat-types.d.ts +32 -0
  113. package/dist-server/service/kpi-stat/kpi-stat-types.js +126 -0
  114. package/dist-server/service/kpi-stat/kpi-stat-types.js.map +1 -0
  115. package/dist-server/service/kpi-value/index.d.ts +3 -0
  116. package/dist-server/service/kpi-value/index.js +7 -0
  117. package/dist-server/service/kpi-value/index.js.map +1 -0
  118. package/dist-server/service/kpi-value/kpi-value-query.d.ts +8 -0
  119. package/dist-server/service/kpi-value/kpi-value-query.js +90 -0
  120. package/dist-server/service/kpi-value/kpi-value-query.js.map +1 -0
  121. package/dist-server/tsconfig.tsbuildinfo +1 -1
  122. package/package.json +8 -7
  123. package/python/RUNNER_CONVERSION_GUIDE.md +81 -0
  124. package/python/doc_summary_engine_handoff/.env.example +21 -0
  125. package/python/doc_summary_engine_handoff/HANDOFF.md +266 -0
  126. package/python/doc_summary_engine_handoff/README.md +71 -0
  127. package/python/doc_summary_engine_handoff/__main__.py +159 -0
  128. package/python/doc_summary_engine_handoff/api.py +165 -0
  129. package/python/doc_summary_engine_handoff/extract.py +290 -0
  130. package/python/doc_summary_engine_handoff/requirements.txt +15 -0
  131. package/python/doc_summary_engine_handoff/summarize.py +247 -0
  132. package/python/inspection_ai_engine_handoff/.env.example +22 -0
  133. package/python/inspection_ai_engine_handoff/HANDOFF.md +541 -0
  134. package/python/inspection_ai_engine_handoff/README.md +83 -0
  135. package/python/inspection_ai_engine_handoff/__main__.py +169 -0
  136. package/python/inspection_ai_engine_handoff/activities.py +147 -0
  137. package/python/inspection_ai_engine_handoff/api.py +273 -0
  138. package/python/inspection_ai_engine_handoff/checklist.py +53 -0
  139. package/python/inspection_ai_engine_handoff/cv_handlers.py +46 -0
  140. package/python/inspection_ai_engine_handoff/cv_model_registry.py +252 -0
  141. package/python/inspection_ai_engine_handoff/cv_models/README.md +58 -0
  142. package/python/inspection_ai_engine_handoff/cv_models/_example_frame_alignment/handler.py +39 -0
  143. package/python/inspection_ai_engine_handoff/cv_models/_example_frame_alignment/model.json +22 -0
  144. package/python/inspection_ai_engine_handoff/engine.py +333 -0
  145. package/python/inspection_ai_engine_handoff/requirements.txt +12 -0
  146. package/python/inspection_ai_engine_handoff/router.py +200 -0
  147. package/python/inspection_ai_engine_handoff/shot_catalog.py +79 -0
  148. package/python/inspection_ai_engine_handoff/shot_planner.py +278 -0
  149. package/python/inspection_ai_engine_handoff/vlm.py +335 -0
  150. package/python/spec-matching-0.5.5/README.md +113 -0
  151. package/python/spec-matching-0.5.5/__main__.py +331 -0
  152. package/python/spec-matching-0.5.5/kcs.csv +6028 -0
  153. package/python/spec-matching-0.5.5/kcs_embeddings.meta.json +1 -0
  154. package/python/spec-matching-0.5.5/kcs_embeddings.npy +0 -0
  155. package/python/spec-matching-0.5.5/requirements.txt +3 -0
  156. package/python/spec-matching-0.5.5//342/230/2057 SF-TD4 /341/204/221/341/205/263/341/204/205/341/205/251/341/204/200/341/205/263/341/204/205/341/205/242/341/206/267/341/204/211/341/205/245/341/206/257/341/204/200/341/205/250/341/204/211/341/205/245 /341/204/213/341/205/243/341/206/274/341/204/211/341/205/265/341/206/250_V1.0.hwp +0 -0
  157. package/schema.graphql +1274 -22
  158. package/things-factory.config.js +7 -1
  159. package/translations/en.json +2 -0
  160. package/translations/ja.json +2 -0
  161. package/translations/ko.json +2 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sv-project-detail.js","sourceRoot":"","sources":["../../client/pages/sv-project-detail.ts"],"names":[],"mappings":"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","sourcesContent":["import { navigate, PageLifecycle, PageView } from '@operato/shell'\nimport { ScrollbarStyles } from '@operato/styles'\nimport { css, html } from 'lit'\nimport { customElement, state } from 'lit/decorators.js'\nimport { ScopedElementsMixin } from '@open-wc/scoped-elements'\nimport { Project as BaseProject } from '@dssp/project/dist-client/pages/project/project-list'\n\n/* dkpi sv-project-list 의 Project 타입과 동일하게 kpiValues 필드 보강 */\ntype Project = BaseProject & { kpiValues?: { kpiName: string; value: number }[] }\nimport { client } from '@operato/graphql'\nimport { getDomain } from '@things-factory/auth-base/dist-client/profiled'\nimport gql from 'graphql-tag'\nimport moment from 'moment-timezone'\nimport { isProjectTypeDomain, tenantHeaders } from '@dssp/project/dist-client/shared/domain-context'\nimport '../components/kpi-boxplot-chart'\nimport '../components/kpi-single-boxplot-chart'\nimport '../components/kpi-radar-chart'\nimport '../components/kpi-lookup-chart'\nimport '../components/kpi-step-lookup-chart'\nimport '../components/kpi-2d-lookup-chart'\n\n@customElement('sv-project-detail')\nexport class SvProjectDetailPage extends ScopedElementsMixin(PageView) {\n static styles = [\n ScrollbarStyles,\n css`\n :host {\n display: flex;\n flex-direction: column;\n overflow-y: auto;\n\n width: 100%;\n height: 100%;\n background-color: var(--md-sys-color-background, #f6f6f6);\n }\n\n /* content layout */\n div[content] {\n display: flex;\n flex-direction: row;\n gap: 35px;\n padding: 25px;\n }\n div[left] {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 20px;\n }\n div[right] {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 20px;\n padding: 0 1px;\n }\n\n /* card */\n .card {\n background: #ffffff;\n border-radius: 10px;\n padding: 15px;\n box-shadow: 3px 3px 3px 0 rgba(0, 0, 0, 0.1);\n }\n .card-title {\n display: flex;\n align-items: center;\n gap: 5px;\n color: #35618e;\n font-weight: 700;\n }\n /* 카드 헤더 우상단 버튼 (프로젝트 수정 / KPI 데이타입력) */\n .card-title .header-btn {\n margin-left: auto;\n padding: 4px 10px;\n font-size: 13px;\n font-weight: 500;\n }\n .card-title .header-btn md-icon {\n --md-icon-size: 18px;\n }\n .triangle {\n width: 0;\n height: 0;\n border-left: 6px solid transparent;\n border-right: 6px solid transparent;\n border-top: 8px solid #35618e;\n }\n\n /* left top info box */\n .bold {\n font-weight: bold;\n }\n .top-info {\n display: flex;\n gap: 10px;\n }\n .top-info img[pic] {\n width: 284px;\n height: 160px;\n object-fit: fill;\n background: #e5e7eb;\n }\n .top-info .info {\n display: flex;\n flex-direction: column;\n gap: 15px;\n padding: 10px 0;\n flex: 1;\n }\n .row {\n display: flex;\n align-items: center;\n gap: 10px;\n }\n .label {\n min-width: fit-content;\n color: #35618e;\n font-size: 16px;\n letter-spacing: -0.05em;\n }\n .value {\n color: #333;\n font-size: 16px;\n }\n .status-value {\n color: #4cbb49;\n font-weight: 700;\n }\n\n .address {\n color: #35618e;\n font-size: 16px;\n letter-spacing: -0.05em;\n }\n\n .detail-grid {\n display: grid;\n grid-template-columns: 284px 1fr;\n gap: 15px 10px;\n padding: 5px 0;\n }\n .detail-grid .grid-item .label {\n width: auto;\n }\n\n /* left second card (requests) */\n .sub-desc {\n color: #35618e;\n font-size: 14px;\n letter-spacing: -0.05em;\n margin-bottom: 8px;\n }\n .list-block {\n display: flex;\n flex-direction: column;\n gap: 5px;\n padding: 5px 25px;\n }\n .list-title {\n color: #212529;\n font-weight: 700;\n font-size: 16px;\n letter-spacing: -0.05em;\n }\n .list-text {\n color: #35618e;\n font-size: 16px;\n letter-spacing: -0.05em;\n margin-left: 8px;\n }\n .list-text .warn {\n margin-left: 10px;\n color: #e13232;\n\n md-icon {\n font-size: 17px;\n width: 13px;\n height: 13px;\n }\n }\n\n /* right buttons */\n .button-line {\n display: flex;\n justify-content: flex-end;\n gap: 10px;\n }\n .ghost-btn {\n display: flex;\n align-items: center;\n gap: 3px;\n padding: 5px 10px;\n background: #35618e;\n color: #ffffff;\n border-radius: 5px;\n box-shadow: 2px 2px 2px 0 rgba(0, 0, 0, 0.1);\n cursor: pointer;\n\n /* Material Symbols Outlined (stroke style) */\n md-icon {\n font-variation-settings:\n 'FILL' 0,\n 'wght' 300,\n 'GRAD' 0,\n 'opsz' 24;\n }\n }\n\n /* right KPI box */\n .kpi-box {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 15px;\n max-height: 420px;\n min-width: 560px;\n }\n .kpi-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n color: #35618e;\n font-weight: 700;\n font-size: 20px;\n letter-spacing: -0.05em;\n }\n .kpi-header div {\n display: flex;\n align-items: center;\n gap: 5px;\n }\n .kpi-desc {\n color: #35618e;\n font-size: 13px;\n letter-spacing: -0.05em;\n }\n .kpi-summary {\n display: flex;\n align-items: center;\n padding: 0 30px;\n max-height: 360px;\n gap: 20px;\n }\n .mini-boxplot {\n flex: 1;\n display: flex;\n flex-direction: column;\n height: 100%;\n position: relative;\n min-width: 120px;\n }\n .boxplot-area {\n flex: 1;\n width: 100%;\n display: flex;\n flex-direction: column;\n align-items: center;\n background: #ffffff;\n }\n\n .boxplot-point {\n display: flex;\n flex-direction: column;\n align-items: center;\n color: #35618e;\n }\n .boxplot-point .value {\n font-size: 42px;\n font-weight: 800;\n color: #35618e;\n border-radius: 6px;\n }\n .boxplot-point .label {\n font-size: 14px;\n font-weight: 300;\n width: auto;\n }\n\n /* spider chart placeholder */\n .spider-area {\n flex: 5;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 10px;\n height: 100%;\n }\n\n /* group distribution */\n .group-box .desc {\n color: #35618e;\n font-size: 13px;\n letter-spacing: -0.05em;\n margin-top: 4px;\n }\n .group-chart {\n height: 300px;\n margin-top: 25px;\n }\n\n kpi-single-boxplot-chart {\n flex: 1;\n }\n\n /* KPI 메트릭 데이터 카드 */\n .metrics-card {\n background: #ffffff;\n border-radius: 10px;\n padding: 20px;\n box-shadow: 3px 3px 3px 0 rgba(0, 0, 0, 0.1);\n max-width: 100%;\n overflow: hidden;\n }\n .metrics-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n }\n .metrics-header .title {\n display: flex;\n align-items: center;\n gap: 8px;\n color: #35618e;\n font-weight: 700;\n font-size: 18px;\n }\n .metrics-card .desc {\n color: #35618e;\n font-size: 13px;\n letter-spacing: -0.05em;\n margin-top: 4px;\n margin-bottom: 15px;\n }\n .metrics-content {\n display: flex;\n flex-direction: column;\n gap: 12px;\n }\n .metric-group {\n border-bottom: 1px dotted #ddd;\n padding-bottom: 12px;\n }\n .metric-group:last-child {\n border-bottom: none;\n padding-bottom: 0;\n }\n .metric-group-header {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 8px;\n color: #35618e;\n font-weight: 600;\n font-size: 16px;\n }\n .metric-group-icon {\n width: 20px;\n height: 20px;\n background: #35618e;\n border-radius: 3px;\n position: relative;\n }\n .metric-group-icon::before {\n content: '✓';\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n color: white;\n font-size: 12px;\n font-weight: bold;\n }\n .metric-items {\n display: grid;\n grid-template-columns: 1fr 1fr 1fr;\n gap: 8px 16px;\n padding-left: 28px;\n }\n .metric-item {\n display: flex;\n justify-content: space-between;\n align-items: center;\n font-size: 14px;\n }\n .metric-label {\n color: #333;\n flex: 1;\n }\n .metric-value {\n color: #35618e;\n font-weight: 600;\n min-width: 40px;\n text-align: right;\n }\n\n /* Popup overlay */\n .popup-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 1000;\n }\n .popup-content {\n background: white;\n border-radius: 12px;\n padding: 24px;\n max-width: 800px;\n width: 90%;\n max-height: 80vh;\n overflow-y: auto;\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);\n }\n .popup-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 20px;\n padding-bottom: 16px;\n border-bottom: 2px solid #e0e0e0;\n }\n .popup-title {\n font-size: 20px;\n font-weight: 700;\n color: #35618e;\n }\n .popup-score {\n display: flex;\n align-items: baseline;\n gap: 6px;\n margin-left: auto;\n margin-right: 16px;\n }\n .popup-score .score-value {\n font-size: 44px;\n font-weight: 800;\n color: #e53935;\n line-height: 1;\n }\n .popup-score .score-label {\n font-size: 14px;\n color: #888;\n }\n .popup-close {\n cursor: pointer;\n font-size: 24px;\n color: #999;\n background: none;\n border: none;\n padding: 4px 8px;\n }\n .popup-close:hover {\n color: #333;\n }\n .popup-chart-container {\n min-height: 400px;\n height: 500px;\n }\n .metric-label {\n cursor: pointer;\n }\n .metric-label:hover {\n opacity: 0.7;\n text-decoration: underline;\n }\n `\n ]\n\n get context() {\n return {\n title: !isProjectTypeDomain() && this.project ? `프로젝트 상세 정보 - ${this.project?.name}` : '프로젝트 상세 정보'\n }\n }\n\n @state() private project: Project = {} as Project\n @state() private kpiComprehensiveStats: any[] = []\n @state() private kpiYComprehensiveStats: any[] = []\n @state() private projectXKpiValues: any[] = []\n @state() private showScoreDistribution: boolean = false\n @state() private selectedKpi: any = null\n /** 이 프로젝트에 아직 한 번도 입력되지 않은 metric (active 한 manual/import 한정) */\n @state() private pendingMetrics: { id: string; name: string; description?: string }[] = []\n /** 현재 프로젝트의 모든 KpiMetricValue (metric.name 포함) — 2D lookup chart 의 progressRate / value 도출용. */\n @state() private allKpiMetricValues: any[] = []\n\n render() {\n return html`\n <div content>\n <div left>\n <div class=\"card kpi-box\">\n <div class=\"kpi-header\">\n <div>\n <div>종합 KPI</div>\n <div class=\"triangle\"></div>\n </div>\n <div class=\"kpi-desc\">프로젝트의 6개 성과를 종합한 종합 KPI포인트와 각 지표별 분포</div>\n </div>\n\n <div class=\"kpi-summary\">\n <div class=\"mini-boxplot\" title=\"boxplot placeholder\">\n <div class=\"boxplot-area\">\n <kpi-single-boxplot-chart .data=${this.getBoxPlotDataForProject(this.project)}></kpi-single-boxplot-chart>\n </div>\n\n <div class=\"boxplot-point\">\n <div class=\"value\">${this.project.kpi ? this.project.kpi.toFixed(1) : '-'}</div>\n <div class=\"label\">Point</div>\n </div>\n </div>\n\n <div class=\"spider-area\">\n <sv-kpi-radar-chart\n .data=${this.getRadarChartData().data}\n .categories=${this.getRadarChartData().categories}\n .currentGroup=${'프로젝트성과'}\n ></sv-kpi-radar-chart>\n </div>\n </div>\n </div>\n\n <div class=\"card group-box\">\n <div class=\"kpi-header\">\n <div>\n <div>성과영역별 KPI</div>\n <div class=\"triangle\"></div>\n </div>\n </div>\n <div class=\"desc\">\n Boxplot(박스플롯)은 박스는 중앙 50% 구간, 수염은 전체 범위, 굵은 파란색 가로선은 중앙값(median), 초록색 원은\n 평균값(Mean)을 의미합니다. 당해 프로젝트의 값은 붉은색 원으로 표시됩니다.\n </div>\n <div class=\"group-chart\">\n <sv-kpi-boxplot-chart .data=${this.getYKpiBoxplotData()}></sv-kpi-boxplot-chart>\n </div>\n </div>\n </div>\n\n <div right>\n <div class=\"metrics-card\">\n <div class=\"metrics-header\">\n <div class=\"title\">\n <span>성과영역별 KPI 측정값</span>\n <div class=\"triangle\"></div>\n </div>\n <div\n class=\"ghost-btn\"\n @click=${async () => {\n if (isProjectTypeDomain()) {\n navigate('project-complete')\n return\n }\n const id = this.project.id || (await this._resolveProjectIdFromDomain())\n if (id) navigate(`project-complete/${id}`)\n }}\n >\n <md-icon>assignment_turned_in</md-icon>\n <div>KPI 데이터 입력</div>\n </div>\n </div>\n <div class=\"desc\">\n 프로젝트의 6개 성과별 상세 주요 매트릭 데이터 (각 세부 항목을 클릭하시면 평가 기준표를 확인할 수 있습니다)\n </div>\n <div class=\"metrics-content\">\n ${Object.values(this.getMetricGroups()).map(\n group => html`\n <div class=\"metric-group\">\n <div class=\"metric-group-header\">\n <div class=\"metric-group-icon\"></div>\n <span>${group.name}</span>\n <span style=\"margin-left: 10px; color: #4caf50; font-weight: 700;\">[${group.score}]</span>\n </div>\n <div class=\"metric-items\">\n ${group.metrics.map(\n metric => html`\n <div class=\"metric-item\">\n <span\n class=\"metric-label\"\n @click=${() => this._onMetricLabelClick(metric)}\n title=\"클릭하시면 평가 기준표를 확인할 수 있습니다\"\n >\n ${metric.label}\n </span>\n <span class=\"metric-value\">${metric.value}</span>\n </div>\n `\n )}\n </div>\n </div>\n `\n )}\n </div>\n </div>\n </div>\n </div>\n\n ${\n this.showScoreDistribution && this.selectedKpi\n ? html`\n <div class=\"popup-overlay\" @click=${this._closePopup}>\n <div class=\"popup-content\" @click=${(e: Event) => e.stopPropagation()}>\n <div class=\"popup-header\">\n <div class=\"popup-title\">${this.selectedKpi.kpiName} — 성과 분포</div>\n ${\n this.selectedKpi.score !== undefined && this.selectedKpi.score !== null\n ? html`\n <div class=\"popup-score\">\n <span class=\"score-value\">${Number(this.selectedKpi.score).toFixed(1)}</span>\n </div>\n `\n : ''\n }\n <button class=\"popup-close\" @click=${this._closePopup}>×</button>\n </div>\n <div class=\"popup-chart-container\">\n ${\n /*\n * scoreType 기반 차트 분기:\n *\n * CUSTOM (X13 등): 2D 룩업 히트맵 — 단, 증강데이터에서는 value가\n * 이미 1~5 정수(룩업 결과)이므로 히트맵 대신 게이지로 폴백.\n * 실제 프로젝트에서는 value가 편차율(소수점)이므로 히트맵 표시.\n *\n * LOOKUP: 성과 분포 커브 (kpi-lookup-chart)\n * ASSESSMENT: 5단계 게이지 (kpi-lookup-chart 자동 감지)\n */ ''\n }\n ${\n // SL-PA 결과(X34 품질, X43 안전) 만 계단형 등급 분포 차트로 표시.\n // (다른 KPI 차트 경로에는 영향 없음)\n this.selectedKpi.pattern === 'X34' || this.selectedKpi.pattern === 'X43'\n ? html`\n <kpi-step-lookup-chart\n .grades=${this.selectedKpi.grades || []}\n .value=${this.selectedKpi.value}\n .score=${this.selectedKpi.score ?? null}\n unit=${this.selectedKpi.unit || ''}\n ></kpi-step-lookup-chart>\n `\n : // 2D lookup KPI (X13 등) 는 항상 등급 기준 + 히트맵 함께 표시 (value 가\n // 정수든 소수든 일관). _renderAssessmentGrades 안의 is2D 분기가 히트맵\n // 같이 그림. 그 외 ASSESSMENT 도 등급 기준, LOOKUP 은 분포 커브.\n this.selectedKpi.scoreType === 'CUSTOM' || this.selectedKpi.grades?.type === 'PROGRESS_DEVIATION_LOOKUP'\n ? this._renderAssessmentGrades()\n : this._isAssessmentOrIntegerScore()\n ? this._renderAssessmentGrades()\n : html`\n <kpi-lookup-chart\n .grades=${this.selectedKpi.grades || []}\n .value=${this.selectedKpi.value}\n .score=${this.selectedKpi.score ?? null}\n .scoreType=${this.selectedKpi.scoreType || ''}\n .valueType=${this.selectedKpi.valueType || ''}\n unit=${this.selectedKpi.unit || ''}\n ></kpi-lookup-chart>\n `\n }\n </div>\n </div>\n </div>\n `\n : ''\n }\n `\n }\n\n async pageUpdated(changes: any, lifecycle: PageLifecycle) {\n if (!this.active) return\n\n let projectId = lifecycle.resourceId\n\n // 프로젝트 테넌트 컨텍스트(/project/<code>/) 에서 진입한 경우 resourceId 가 비어있다.\n // 현재 도메인의 subdomain(= Project.code) 으로 프로젝트를 역조회.\n if (!projectId) {\n projectId = await this._resolveProjectIdFromDomain()\n }\n\n if (!projectId) return\n\n // initProject 의 GraphQL 응답을 기다리지 않고 project.id 를 즉시 채워서,\n // 링크의 `project-update/${project.id}` 가 절대 undefined 가 되지 않도록 보장.\n this.project = { ...this.project, id: projectId } as Project\n\n // 후속 query 들이 tenant 컨텍스트로 동작하도록 project.code 를 먼저 가볍게 resolve.\n // (tenant 컨텍스트에서 진입한 경우는 헤더 불필요 — tenantHeaders 가 빈 객체 반환.)\n if (!isProjectTypeDomain()) {\n await this._resolveProjectCode(projectId)\n }\n\n this.initProject(projectId)\n this.getKpiComprehensiveStats()\n this.getKpiYComprehensiveStats()\n this.getProjectXKpiValues(projectId)\n }\n\n private async _resolveProjectCode(projectId: string): Promise<void> {\n try {\n const resp = await client.query({\n query: gql`\n query ProjectCode($id: String!) {\n project(id: $id) {\n id\n code\n }\n }\n `,\n variables: { id: projectId }\n })\n const code = resp.data?.project?.code\n if (code) {\n this.project = { ...this.project, code } as Project\n }\n } catch (e) {\n console.warn('[project-detail] project.code resolve 실패', e)\n }\n }\n\n private async _resolveProjectIdFromDomain(): Promise<string> {\n const response = await client.query({\n query: gql`\n query CurrentProject {\n currentProject {\n id\n }\n }\n `\n })\n\n return response.data?.currentProject?.id || ''\n }\n\n async initProject(projectId: string = '') {\n const response = await client.query({\n query: gql`\n query Project($id: String!) {\n project(id: $id) {\n id\n code\n name\n state\n startDate\n endDate\n projectType\n sectorType\n buildingUsage\n mainPhoto {\n fullpath\n }\n totalProgress\n weeklyProgress\n kpi\n kpiValues {\n kpiName\n value\n }\n inspPassRate\n robotProgressRate\n structuralSafetyRate\n robotCount\n buildingComplex {\n id\n address\n latitude\n longitude\n area\n coverageRatio\n floorAreaRatio\n siteType\n workerCount\n clientCompany\n constructionCompany\n supervisoryCompany\n designCompany\n drawing {\n id\n name\n fullpath\n }\n constructionType\n constructionCost\n etc\n notice\n householdCount\n buildingCount\n virtualTourLink\n buildings {\n id\n name\n floorCount\n }\n }\n }\n }\n `,\n variables: { id: projectId },\n context: { headers: tenantHeaders((this.project as any)?.code) }\n })\n\n if (response.errors) return\n\n this.project = response.data?.project || ({} as Project)\n\n this.updateContext()\n this._loadPendingMetrics()\n }\n\n /**\n * 이 프로젝트의 미입력 KPI metric 들을 조회 — Step 1/2 와 동일 기준.\n * - 대상: active=true 인 manual/import metric (자동수집/외부API 제외)\n * - 판정:\n * · MONTH metric → 전월 row (periodType='MONTH', valueDate startsWith 전월) 없으면 pending\n * · 그 외 → (metric, periodType) 매칭 row 없으면 pending\n */\n private async _loadPendingMetrics() {\n if (!this.project?.id) {\n this.pendingMetrics = []\n return\n }\n\n try {\n // 1) Active manual/import metric 정의 목록 (periodType 포함)\n const metricsResp = await client.query({\n query: gql`\n query KpiMetrics {\n kpiMetrics {\n items {\n id\n name\n description\n periodType\n collectType\n active\n }\n }\n }\n `,\n context: { headers: tenantHeaders((this.project as any)?.code) }\n })\n // collectType 필터 제거 — Step 1/2 가 모든 metric 을 보여주므로 동일하게 전체 대상.\n // active=false 인 metric 만 제외.\n const targets = (metricsResp.data?.kpiMetrics?.items || []).filter((m: any) => m.active)\n if (targets.length === 0) {\n this.pendingMetrics = []\n return\n }\n\n // 2) 이 프로젝트의 모든 KpiMetricValue (metric.name 포함 — 2D 차트의 latest lookup 에도 사용)\n const valuesResp = await client.query({\n query: gql`\n query KpiMetricValues($filters: [Filter!]) {\n kpiMetricValues(filters: $filters) {\n items {\n metricId\n periodType\n valueDate\n value\n metric {\n id\n name\n }\n }\n }\n }\n `,\n variables: { filters: [{ name: 'org', operator: 'eq', value: this.project.id }] },\n context: { headers: tenantHeaders((this.project as any)?.code) }\n })\n const values: any[] = valuesResp.data?.kpiMetricValues?.items || []\n this.allKpiMetricValues = values\n const lastMonthYm = moment().tz('Asia/Seoul').subtract(1, 'month').format('YYYY-MM')\n\n // 3) Step 1/2 와 동일 기준으로 미입력 판정 — row 존재 AND value 존재 (null/undefined 아님).\n const hasValid = (v: any) => v.value !== null && v.value !== undefined\n this.pendingMetrics = targets.filter((m: any) => {\n if (m.periodType === 'MONTH') {\n return !values.some(\n v => v.metricId === m.id && v.periodType === 'MONTH' && (v.valueDate || '').startsWith(lastMonthYm) && hasValid(v)\n )\n }\n return !values.some(v => v.metricId === m.id && v.periodType === m.periodType && hasValid(v))\n })\n } catch (e) {\n console.error('[입력 대기 중] 조회 실패', e)\n this.pendingMetrics = []\n }\n }\n\n async getKpiComprehensiveStats() {\n try {\n const response = await client.query({\n query: gql`\n query GetKpiZValueComprehensiveStats {\n totalKpiZValueComprehensiveStats {\n minVal\n q1Val\n medVal\n q3Val\n maxVal\n }\n }\n `,\n context: { headers: tenantHeaders((this.project as any)?.code) }\n })\n\n this.kpiComprehensiveStats = response.data.totalKpiZValueComprehensiveStats || []\n } catch (error) {\n console.error('Failed to fetch KPI comprehensive stats:', error)\n this.kpiComprehensiveStats = []\n }\n }\n\n async getKpiYComprehensiveStats() {\n try {\n const response = await client.query({\n query: gql`\n query GetKpiYValueComprehensiveStats {\n totalKpiYValueComprehensiveStats {\n kpiName\n minVal\n q1Val\n medVal\n q3Val\n maxVal\n avgVal\n projectCount\n }\n }\n `,\n context: { headers: tenantHeaders((this.project as any)?.code) }\n })\n\n this.kpiYComprehensiveStats = response.data.totalKpiYValueComprehensiveStats || []\n } catch (error) {\n console.error('Failed to fetch KPI Y comprehensive stats:', error)\n this.kpiYComprehensiveStats = []\n }\n }\n\n async getProjectXKpiValues(projectId: string) {\n try {\n const response = await client.query({\n query: gql`\n query GetProjectXKpiValues($projectId: String!) {\n projectXKpiValues(projectId: $projectId) {\n id\n value\n score\n valueDate\n kpi {\n id\n name\n description\n }\n }\n }\n `,\n variables: { projectId },\n context: { headers: tenantHeaders((this.project as any)?.code) }\n })\n\n this.projectXKpiValues = response.data.projectXKpiValues || []\n } catch (error) {\n console.error('Failed to fetch project X KPI values:', error)\n this.projectXKpiValues = []\n }\n }\n\n private _onMetricLabelClick(metric: any) {\n // metric.pattern을 사용하여 X-KPI 조회\n if (!metric.pattern) {\n console.warn('No pattern found for metric:', metric)\n return\n }\n\n // X-KPI 이름 패턴 (예: X11, X12, X21, ...)\n const kpiNamePattern = metric.pattern\n\n // 해당 KPI 정보 조회\n this._fetchKpiWithGradesByPattern(kpiNamePattern, metric.label)\n }\n\n private async _fetchKpiWithGradesByPattern(kpiNamePattern: string, displayName: string) {\n try {\n // X-KPI 이름 패턴으로 전체 이름 찾기 (projectXKpiValues에서) — 선두 코드 토큰 정확 매칭\n const codeOf = (name: string) => (name || '').trim().split(/[.\\s]/)[0]\n const kpiValue = this.projectXKpiValues.find(kv => kv.kpi?.name && codeOf(kv.kpi.name) === kpiNamePattern)\n\n if (!kpiValue || !kpiValue.kpi) {\n console.warn(`No KPI found for pattern: ${kpiNamePattern}`)\n return\n }\n\n // GraphQL로 KPI 상세 정보 (grades 포함) 조회\n const response = await client.query({\n query: gql`\n query Kpi($id: String!) {\n kpi(id: $id) {\n id\n name\n grades\n scoreType\n valueType\n vizMeta\n }\n }\n `,\n variables: { id: kpiValue.kpi.id },\n context: { headers: tenantHeaders((this.project as any)?.code) }\n })\n\n if (response.errors || !response.data?.kpi) {\n console.error('KPI not found or error:', response.errors)\n return\n }\n\n const kpi = response.data.kpi\n\n // grades가 없으면 경고\n const is2D = kpi.grades && !Array.isArray(kpi.grades) && kpi.grades.type === 'PROGRESS_DEVIATION_LOOKUP'\n if (!kpi.grades || (!is2D && (!Array.isArray(kpi.grades) || kpi.grades.length === 0))) {\n console.warn(`No grades found for KPI: ${kpiValue.kpi.name}`)\n }\n\n this.selectedKpi = {\n kpiName: displayName,\n pattern: kpiNamePattern, // X34/X43 등 — 계단형 차트 라우팅용\n grades: kpi.grades || [],\n scoreType: kpi.scoreType || '',\n valueType: kpi.valueType || '',\n value: kpiValue.value ?? null,\n score: kpiValue.score ?? null,\n unit: kpi.vizMeta?.unit || ''\n }\n\n this.showScoreDistribution = true\n } catch (error) {\n console.error('Error fetching KPI:', error)\n }\n }\n\n /**\n * 등급 기준으로 표시할지 판별 (차트 대신 등급 설명 목록)\n *\n * - valueType=ASSESSED: 감리자 직접 평가 → 등급 설명\n * - CUSTOM/COMPOSITE이지만 value가 1~5 정수 (증강데이터): 등급 설명 + 히트맵\n * - grades가 배열이 아닌 경우 (2D lookup 객체): 등급 설명 + 히트맵\n */\n private _isAssessmentOrIntegerScore(): boolean {\n const kpi = this.selectedKpi\n if (!kpi) return false\n // valueType 기반 판별 (우선)\n if (kpi.valueType === 'ASSESSED') return true\n // CUSTOM/COMPOSITE이지만 value가 1~5 정수 (증강데이터)\n if (\n (kpi.scoreType === 'CUSTOM' || kpi.valueType === 'COMPOSITE') &&\n kpi.value !== null &&\n kpi.value === Math.floor(kpi.value) &&\n kpi.value >= 1 &&\n kpi.value <= 5\n )\n return true\n // grades가 배열이 아닌 경우 (2D lookup 객체)\n if (kpi.grades && !Array.isArray(kpi.grades)) return true\n return false\n }\n\n /** 등급 기준 표시 (ASSESSED / 1~5 정수 score) — 현재 값 강조 + CUSTOM은 2D 히트맵도 표시 */\n private _renderAssessmentGrades() {\n const kpi = this.selectedKpi\n // score 가 진짜 등급. kpi.value 는 raw 측정값 (편차율 등) 이라 score 로 쓰면 안 됨.\n const currentScore = kpi?.score != null ? Math.round(kpi.score) : 0\n const grades = Array.isArray(kpi?.grades) ? kpi.grades : []\n const colors = ['#e53935', '#ff9800', '#ffca28', '#66bb6a', '#2e7d32']\n const is2D =\n kpi?.scoreType === 'CUSTOM' ||\n kpi?.valueType === 'COMPOSITE' ||\n (kpi?.grades &&\n typeof kpi.grades === 'object' &&\n !Array.isArray(kpi.grades) &&\n kpi.grades.type === 'PROGRESS_DEVIATION_LOOKUP')\n\n // grades가 있으면 description 사용, 없으면 기본 라벨\n const defaultLabels = ['매우 미흡', '미흡', '보통', '양호', '우수']\n const items = [1, 2, 3, 4, 5].map(score => {\n const grade = grades.find((g: any) => g.score === score)\n return {\n score,\n desc: grade?.description || grade?.name || defaultLabels[score - 1],\n color: colors[score - 1],\n active: score === currentScore\n }\n })\n\n // ASSESSED(감리자 평가)이고 grades에 description이 있는 경우만 한 줄씩 상세 표시\n // X13 등 COMPOSITE/CUSTOM은 인라인 칩으로 간결하게\n const hasDescriptions = !is2D && grades.length > 0 && grades.some((g: any) => g.description)\n\n // ASSESSMENT (감리자 평가) 의 등급 description 만 노출. 2D lookup 은 score 가 이미\n // popup-header 큰 점수로 표시되므로 등급 기준 행/동그라미 중복 제거.\n return html`\n <div style=\"padding: 20px; display: flex; flex-direction: column; height: 100%;\">\n ${\n hasDescriptions\n ? items.map(\n item =>\n html` <div\n style=\"\n display: flex; align-items: center; gap: 10px;\n padding: 10px 14px; margin-bottom: 6px;\n border-radius: 8px;\n background: ${item.active ? item.color + '18' : '#f9f9f9'};\n border: 2px solid ${item.active ? item.color : 'transparent'};\n transition: all 0.2s;\n \"\n >\n <span\n style=\"\n width: 32px; height: 32px; border-radius: 50%;\n background: ${item.active ? item.color : '#e0e0e0'};\n color: ${item.active ? '#fff' : '#999'};\n display: flex; align-items: center; justify-content: center;\n font-weight: 700; font-size: 14px; flex-shrink: 0;\n \"\n >${item.score}</span\n >\n <span\n style=\"font-size: 0.9rem; color: ${item.active ? '#333' : '#666'}; font-weight: ${\n item.active ? '600' : '400'\n };\"\n >\n ${item.desc}\n </span>\n </div>`\n )\n : ''\n }\n ${\n is2D && kpi?.grades?.rows\n ? html`\n <div style=\"flex: 1; display: flex; flex-direction: column; min-height: 0;\">\n <div style=\"font-size: 0.9rem; font-weight: 700; color: #333; margin-bottom: 8px;\">\n 공정률 × 공기편차 성과 분포\n </div>\n <div style=\"flex: 1; min-height: 0;\">\n <kpi-2d-lookup-chart\n .grades=${kpi.grades}\n .value=${this._getScheduleDeviation()}\n .progressRate=${this._getProgressRate()}\n ></kpi-2d-lookup-chart>\n </div>\n </div>\n `\n : ''\n }\n </div>\n `\n }\n\n private _closePopup() {\n this.showScoreDistribution = false\n this.selectedKpi = null\n }\n\n private getMetricValue(kpiNamePattern: string): string {\n // KPI 이름 \"X52. 환경성과 수준 평가\" 의 선두 코드 토큰(\"X52\")을 정확히 매칭.\n // includes 부분매칭은 유사코드 오매칭 위험이 있어 코드 토큰 === 로 비교한다.\n const codeOf = (name: string) => (name || '').trim().split(/[.\\s]/)[0]\n const matches = this.projectXKpiValues.filter(kv => kv.kpi?.name && codeOf(kv.kpi.name) === kpiNamePattern)\n // 같은 코드가 여러 scope/기간으로 중복될 수 있어 최신 valueDate 를 대표값으로 선택.\n const kpiValue = matches.sort((a, b) => (b.valueDate || '').localeCompare(a.valueDate || ''))[0]\n const value = kpiValue?.value || 0\n\n // 성과영역별 KPI 측정값: 정수는 그대로, 소수는 소숫점 2자리로 표시\n if (Number.isInteger(value)) {\n return value.toString()\n } else {\n return value.toFixed(2)\n }\n }\n\n private getYKpiScoreFormatted(kpiName: string): string {\n const value = this.getProjectYKpiValue(kpiName)\n if (value === 0) return '-'\n return (value * 100).toFixed(1)\n }\n\n private getMetricGroups() {\n return {\n Y1: {\n name: '일정성과 (Y1)',\n score: this.getYKpiScoreFormatted('일정성과(Y1)'),\n metrics: [\n { label: '연면적 대비 공사기간', pattern: 'X11', value: this.getMetricValue('X11') },\n { label: '설계변경 공기 증감률', pattern: 'X12', value: this.getMetricValue('X12') },\n { label: '일정 이탈 수준', pattern: 'X13', value: this.getMetricValue('X13') },\n { label: '일정성과 정성 평가', pattern: 'X14', value: this.getMetricValue('X14') }\n ]\n },\n Y2: {\n name: '비용성과 (Y2)',\n score: this.getYKpiScoreFormatted('비용성과(Y2)'),\n metrics: [\n { label: '연면적 대비 공사비', pattern: 'X21', value: this.getMetricValue('X21') },\n { label: '설계변경 공사비 증감률', pattern: 'X22', value: this.getMetricValue('X22') },\n { label: '비용성과 정성 평가', pattern: 'X23', value: this.getMetricValue('X23') }\n ]\n },\n Y3: {\n name: '품질성과 (Y3)',\n score: this.getYKpiScoreFormatted('품질성과(Y3)'),\n metrics: [\n { label: '검측 불합격률', pattern: 'X33', value: this.getMetricValue('X33') },\n { label: '품질 SL-PA 결과', pattern: 'X34', value: this.getMetricValue('X34') },\n { label: '품질성과 정성 평가', pattern: 'X35', value: this.getMetricValue('X35') }\n ]\n },\n Y4: {\n name: '안전성과 (Y4)',\n score: this.getYKpiScoreFormatted('Y4. 안전성과'),\n metrics: [\n { label: '재해율', pattern: 'X41', value: this.getMetricValue('X41') },\n { label: '재해강도율', pattern: 'X42', value: this.getMetricValue('X42') },\n { label: '안전 SL-PA 결과', pattern: 'X43', value: this.getMetricValue('X43') },\n { label: '안전성과 정성 평가', pattern: 'X44', value: this.getMetricValue('X44') }\n ]\n },\n Y5: {\n name: '환경성과 (Y5)',\n score: this.getYKpiScoreFormatted('환경성과(Y5)'),\n metrics: [\n { label: '건설폐기물 발생량', pattern: 'X51', value: this.getMetricValue('X51') },\n { label: '환경성과 정성 평가', pattern: 'X52', value: this.getMetricValue('X52') }\n ]\n },\n Y6: {\n name: '생산성 성과 (Y6)',\n score: this.getYKpiScoreFormatted('생산성성과(Y6)'),\n metrics: [\n { label: '총 투입인력(M/M) 생산성', pattern: 'X61', value: this.getMetricValue('X61') },\n { label: '생산성성과 정성 평가', pattern: 'X62', value: this.getMetricValue('X62') }\n ]\n }\n }\n }\n\n private getYKpiBoxplotData() {\n // kpiName 표기가 환경마다 다양('Y1. 일정성과' / 'Y1. 일정' / '일정성과(Y1)' 등)하므로\n // Y1~Y6 코드만 추출해 짧은 X축 라벨로 매핑 — 전체 문자열에 의존하지 않아 견고함.\n const yLabelByCode: Record<string, string> = {\n Y1: '일정',\n Y2: '비용',\n Y3: '품질',\n Y4: '안전',\n Y5: '환경',\n Y6: '생산성'\n }\n\n const data: Array<{\n org: string\n min: number\n max: number\n q1: number\n q3: number\n median: number\n mean: number\n value: number\n }> = []\n\n // Y-KPI 카테고리별로 통합된 통계 생성\n this.kpiYComprehensiveStats.forEach(stat => {\n const category = yLabelByCode[(stat.kpiName || '').match(/Y[1-6]/)?.[0] ?? ''] || stat.kpiName\n\n data.push({\n org: category, // org 필드 사용 (boxplot 차트가 기대하는 필드명)\n min: stat.minVal * 100,\n max: stat.maxVal * 100,\n q1: stat.q1Val * 100,\n q3: stat.q3Val * 100,\n median: stat.medVal * 100,\n mean: stat.avgVal * 100,\n value: this.getProjectYKpiValue(stat.kpiName) * 100 // 현재 프로젝트의 실제 Y-KPI 값 (0~1 → 0~20)\n })\n })\n\n // 가나다순(localeCompare)이 아니라 Y1~Y6 고정 순서로 정렬 (일정·비용·품질·안전·환경·생산성)\n const ORDER = ['일정', '비용', '품질', '안전', '환경', '생산성']\n return data.sort((a, b) => ORDER.indexOf(a.org) - ORDER.indexOf(b.org))\n }\n\n /**\n * KpiMetric.name 이 주어진 이름인 KpiMetricValue 중 가장 최근 valueDate 의 value.\n * 2D lookup chart 의 progressRate / value 도출용.\n */\n private _getLatestMetricValue(metricName: string): number | null {\n const candidates = this.allKpiMetricValues\n .filter(v => v.metric?.name === metricName && v.value !== null && v.value !== undefined)\n .sort((a, b) => (b.valueDate || '').localeCompare(a.valueDate || ''))\n return candidates.length > 0 ? candidates[0].value : null\n }\n\n /** 2D lookup chart 의 progressRate (X축, 실적공정율 %) — 이름이 '실적공정' 을 포함하는\n * metric 의 latest 값 우선(율/률·공백·코드 접두 차이 허용, '계획공정' 과는 구분). 없으면 fallback. */\n private _getProgressRate(): number {\n const candidates = this.allKpiMetricValues\n // 월별(MONTH) 행만 — 옛 일별(DAY) 잔재가 valueDate 로 더 최신이라 잘못 집히던 것 방지.\n .filter(\n v => (v.metric?.name || '').includes('실적공정') && v.periodType === 'MONTH' && v.value !== null && v.value !== undefined\n )\n .sort((a, b) => (b.valueDate || '').localeCompare(a.valueDate || ''))\n if (candidates.length > 0) return candidates[0].value\n return (this.project as any)?.totalProgress ?? 50\n }\n\n /** 2D lookup chart 의 value (Y축, 공기편차) — X13(일정 이탈 수준)이 formula\n * (`[계획공정률] − [실적공정률]`)로 계산한 값을 그대로 사용한다. 산식은 X13 한 곳에만\n * 두고, 클라는 재계산하지 않는다(단일 소스). 값이 없으면 '일정 이탈 수준' metric 폴백. */\n private _getScheduleDeviation(): number | null {\n if (this.selectedKpi?.value !== undefined && this.selectedKpi?.value !== null) return this.selectedKpi.value\n return this._getLatestMetricValue('일정 이탈 수준')\n }\n\n private getProjectYKpiValue(kpiName: string): number {\n // kpiName 표기가 'Y1. 일정성과' / '일정성과(Y1)' 등으로 달라 정확 매칭은 자주 실패함.\n // Y1~Y6 코드만 추출해 매칭 → 형식 차이와 무관하게 값 조회.\n const code = (kpiName || '').match(/Y[1-6]/)?.[0]\n const kpiValue = this.project?.kpiValues?.find(kv =>\n code ? (kv.kpiName || '').match(/Y[1-6]/)?.[0] === code : kv.kpiName === kpiName\n )\n return kpiValue?.value || 0\n }\n\n private getRadarChartData() {\n // kpiName 표기가 다양하므로 Y1~Y6 코드만 추출해 짧은 라벨로 매핑 (박스플롯과 동일 방식).\n const yLabelByCode: Record<string, string> = {\n Y1: '일정',\n Y2: '비용',\n Y3: '품질',\n Y4: '안전',\n Y5: '환경',\n Y6: '생산성'\n }\n\n const data: Array<{ org: string; category: string; value: number }> = []\n const categories: string[] = []\n\n // Create baseline data from comprehensive stats (using avgVal as baseline)\n this.kpiYComprehensiveStats.forEach(stat => {\n const category = yLabelByCode[(stat.kpiName || '').match(/Y[1-6]/)?.[0] ?? ''] || stat.kpiName\n if (!categories.includes(category)) {\n categories.push(category)\n }\n\n // Add baseline average\n // 성과영역별 측정값(getYKpiScoreFormatted, toFixed(1))과 소수 1자리로 일치시킨다.\n // 과거엔 Math.round(정수)라 radar 76 vs 측정값 76.2 처럼 반올림 불일치가 있었음.\n data.push({\n org: '기준평균',\n category,\n value: Number((stat.avgVal * 100).toFixed(1)) // Scale to match expected range (0~1 → 0~100)\n })\n\n // Add current project performance (using project's individual values)\n data.push({\n org: '프로젝트성과',\n category,\n value: Number((this.getProjectYKpiValue(stat.kpiName) * 100).toFixed(1)) // Y-KPI 값 (0~1 → 0~100)\n })\n })\n\n return {\n data,\n categories: categories.length > 0 ? categories : ['일정', '비용', '품질', '안전', '환경', '생산성']\n }\n }\n\n private getBoxPlotDataForProject(project: Project) {\n // 프로젝트의 지역(geo_group)에 해당하는 통계 찾기\n const stats = this.kpiComprehensiveStats?.[0]\n\n if (!stats) {\n // 전체 통계의 평균값 사용 또는 기본값 반환\n const defaultStats = this.kpiComprehensiveStats.length > 0 ? this.kpiComprehensiveStats[0] : null\n if (defaultStats) {\n return {\n min: defaultStats.minVal,\n max: defaultStats.maxVal,\n mean: (defaultStats.q1Val + defaultStats.q3Val) / 2,\n median: defaultStats.medVal,\n q1: defaultStats.q1Val,\n q3: defaultStats.q3Val,\n value: project.kpi || 0\n }\n }\n\n // 완전한 기본값\n return {\n min: 0,\n max: 100,\n mean: 50,\n median: 50,\n q1: 25,\n q3: 75,\n value: project.kpi || 0\n }\n }\n\n return {\n min: stats.minVal * 100,\n max: stats.maxVal * 100,\n mean: ((stats.q1Val + stats.q3Val) / 2) * 100, // 대략적 평균값\n median: stats.medVal * 100,\n q1: stats.q1Val * 100,\n q3: stats.q3Val * 100,\n value: project.kpi || 0\n }\n }\n}\n"]}
@@ -1 +1 @@
1
- export default function route(page: string): "/project-detail" | "/dashboard" | "users" | undefined;
1
+ export default function route(page: string): "kpi-overview" | "kpi-dashboard-map" | "kpi-system-guide" | "project-complete" | "/project-detail" | "/dashboard" | "users" | "kpi-project-detail" | undefined;
@@ -15,6 +15,21 @@ export default function route(page) {
15
15
  case 'users':
16
16
  import('./pages/sv-user-management');
17
17
  return page;
18
+ case 'kpi-dashboard-map':
19
+ import('./pages/kpi-dashboard/kpi-dashboard-map');
20
+ return page;
21
+ case 'kpi-overview':
22
+ import('./pages/kpi-admin/dssp-kpi-overview');
23
+ return page;
24
+ case 'kpi-system-guide':
25
+ import('./pages/kpi-admin/kpi-system-guide');
26
+ return page;
27
+ case 'kpi-project-detail':
28
+ import('./pages/sv-project-detail');
29
+ return page;
30
+ case 'project-complete':
31
+ import('./pages/sv-project-complete');
32
+ return page;
18
33
  }
19
34
  }
20
35
  //# sourceMappingURL=route.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"route.js","sourceRoot":"","sources":["../client/route.ts"],"names":[],"mappings":"AAAA,SAAS,aAAa;IACpB,6CAA6C;IAC7C,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAA;IACnE,OAAO,CAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAG,CAAC,CAAC,KAAI,QAAQ,CAAA;AAC/B,CAAC;AAED,MAAM,CAAC,OAAO,UAAU,KAAK,CAAC,IAAY;IACxC,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;QAChB,MAAM,UAAU,GAAG,aAAa,EAAE,CAAA;QAClC,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;YAC7B,OAAO,iBAAiB,CAAA;QAC1B,CAAC;QACD,OAAO,YAAY,CAAA;IACrB,CAAC;IAED,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,OAAO;YACV,MAAM,CAAC,4BAA4B,CAAC,CAAA;YACpC,OAAO,IAAI,CAAA;IACf,CAAC;AACH,CAAC","sourcesContent":["function getDomainType(): string {\n // URL pattern: /{extType}/{subdomain}/<rest>\n const match = window.location.pathname.match(/^\\/([^\\/]+)\\/[^\\/]+/)\n return match?.[1] || 'domain'\n}\n\nexport default function route(page: string) {\n if (page === '') {\n const domainType = getDomainType()\n if (domainType === 'project') {\n return '/project-detail'\n }\n return '/dashboard'\n }\n\n switch (page) {\n case 'users':\n import('./pages/sv-user-management')\n return page\n }\n}\n"]}
1
+ {"version":3,"file":"route.js","sourceRoot":"","sources":["../client/route.ts"],"names":[],"mappings":"AAAA,SAAS,aAAa;IACpB,6CAA6C;IAC7C,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAA;IACnE,OAAO,CAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAG,CAAC,CAAC,KAAI,QAAQ,CAAA;AAC/B,CAAC;AAED,MAAM,CAAC,OAAO,UAAU,KAAK,CAAC,IAAY;IACxC,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;QAChB,MAAM,UAAU,GAAG,aAAa,EAAE,CAAA;QAClC,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;YAC7B,OAAO,iBAAiB,CAAA;QAC1B,CAAC;QACD,OAAO,YAAY,CAAA;IACrB,CAAC;IAED,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,OAAO;YACV,MAAM,CAAC,4BAA4B,CAAC,CAAA;YACpC,OAAO,IAAI,CAAA;QAEb,KAAK,mBAAmB;YACtB,MAAM,CAAC,yCAAyC,CAAC,CAAA;YACjD,OAAO,IAAI,CAAA;QAEb,KAAK,cAAc;YACjB,MAAM,CAAC,qCAAqC,CAAC,CAAA;YAC7C,OAAO,IAAI,CAAA;QAEb,KAAK,kBAAkB;YACrB,MAAM,CAAC,oCAAoC,CAAC,CAAA;YAC5C,OAAO,IAAI,CAAA;QAEb,KAAK,oBAAoB;YACvB,MAAM,CAAC,2BAA2B,CAAC,CAAA;YACnC,OAAO,IAAI,CAAA;QAEb,KAAK,kBAAkB;YACrB,MAAM,CAAC,6BAA6B,CAAC,CAAA;YACrC,OAAO,IAAI,CAAA;IACf,CAAC;AACH,CAAC","sourcesContent":["function getDomainType(): string {\n // URL pattern: /{extType}/{subdomain}/<rest>\n const match = window.location.pathname.match(/^\\/([^\\/]+)\\/[^\\/]+/)\n return match?.[1] || 'domain'\n}\n\nexport default function route(page: string) {\n if (page === '') {\n const domainType = getDomainType()\n if (domainType === 'project') {\n return '/project-detail'\n }\n return '/dashboard'\n }\n\n switch (page) {\n case 'users':\n import('./pages/sv-user-management')\n return page\n\n case 'kpi-dashboard-map':\n import('./pages/kpi-dashboard/kpi-dashboard-map')\n return page\n\n case 'kpi-overview':\n import('./pages/kpi-admin/dssp-kpi-overview')\n return page\n\n case 'kpi-system-guide':\n import('./pages/kpi-admin/kpi-system-guide')\n return page\n\n case 'kpi-project-detail':\n import('./pages/sv-project-detail')\n return page\n\n case 'project-complete':\n import('./pages/sv-project-complete')\n return page\n }\n}\n"]}
@@ -0,0 +1,45 @@
1
+ export declare const getProject: (projectId: string, projectCode?: string) => Promise<any>;
2
+ export declare const getKpiMetrics: (projectCode?: string) => Promise<any>;
3
+ export declare const getKpiMetricValues: (projectId: string, projectCode?: string) => Promise<any>;
4
+ export declare const getKpiCategories: (projectCode?: string) => Promise<any>;
5
+ /**
6
+ * BuildingComplex 컬럼 부분 업데이트. updateBuildingComplex 는 기존 row 를 load 후
7
+ * patch 를 merge 저장하므로 (building-complex-mutation.ts) 지정 컬럼만 안전하게 갱신
8
+ * — updateProject 처럼 buildings 재생성 side-effect 가 없다.
9
+ */
10
+ export declare const updateBuildingComplexFields: (id: string, patch: any, projectCode?: string) => Promise<import("@apollo/client").InteropMutateResult<any>>;
11
+ export declare const updateProjectCompleteStep1: (patches: any, projectCode?: string) => Promise<import("@apollo/client").InteropMutateResult<any>>;
12
+ export declare const updateProjectCompleteStep2: (patches: any, projectCode?: string) => Promise<import("@apollo/client").InteropMutateResult<any>>;
13
+ export declare const updateProjectCompleteStep3: (file: any, projectId: string, projectCode?: string) => Promise<import("@apollo/client").InteropMutateResult<any>>;
14
+ /**
15
+ * UI "자동 수집" 버튼 전용 — 세움터/올바로/키스콘 시나리오를 순차 호출 후
16
+ * 시스템별 성공/실패 결과를 반환. 시나리오는 도메인 EnvVar 만으로 동작하므로
17
+ * 파라미터 없음. 시나리오 내부 step 이 KPI Value 를 DB 에 적재하므로 호출 후
18
+ * KPI Metric Values 를 다시 조회해 form 을 갱신하는 것이 정상 흐름.
19
+ */
20
+ export interface ExternalDataCollectionResult {
21
+ source: string;
22
+ label: string;
23
+ ok: boolean;
24
+ message: string;
25
+ /**
26
+ * 시나리오 result 객체. projectKey: value 맵.
27
+ * - 세움터: { area, floorAreaRatio, upperFloorCount, lowerFloorCount,
28
+ * structureType, siteType }
29
+ * - 올바로: { totalConstructionWasteAmount }
30
+ * - 키스콘: { constructionPeriod, constructionCost }
31
+ *
32
+ * siteType (건물 용도, SiteType enum 값 — 예: APARTMENT_COMPLEX) 와
33
+ * structureType (구조형태) 는 KPI metric 이 아니라 BuildingComplex 의 기본
34
+ * 속성으로 사용. KISCON 의 "공사유형(신설/증축)" 은 SiteType 도메인과 다르므로
35
+ * 매핑 대상 아님.
36
+ *
37
+ * 없는 키는 omit. 시나리오 실패 시 null/undefined.
38
+ */
39
+ data?: Record<string, number | string | null>;
40
+ }
41
+ export declare const collectProjectExternalData: (projectId: string, projectCode?: string) => Promise<ExternalDataCollectionResult[]>;
42
+ export declare const updateProjectCompleteFinalize: (projectId: string, projectCode?: string) => Promise<import("@apollo/client").InteropMutateResult<any>>;
43
+ /** 완료(COMPLETED) 프로젝트를 진행중(ONGOING) 으로 되돌림 (완료처리 토글의 해제). */
44
+ export declare const updateProjectCompleteUnfinalize: (projectId: string, projectCode?: string) => Promise<import("@apollo/client").InteropMutateResult<any>>;
45
+ export declare const recalculateProjectKpi: (projectId: string, projectCode?: string) => Promise<import("@apollo/client").InteropMutateResult<any>>;
@@ -0,0 +1,274 @@
1
+ import { gql } from '@apollo/client';
2
+ import { client } from '@operato/graphql';
3
+ import { tenantHeaders } from '@dssp/project/dist-client/shared/domain-context';
4
+ export const getProject = async (projectId, projectCode) => {
5
+ const response = await client.query({
6
+ query: gql `
7
+ query Project($id: String!) {
8
+ project(id: $id) {
9
+ id
10
+ code
11
+ name
12
+ startDate
13
+ endDate
14
+ state
15
+
16
+ completeReport {
17
+ id
18
+ name
19
+ fullpath
20
+ }
21
+
22
+ buildingComplex {
23
+ id
24
+ coverageRatio
25
+ floorAreaRatio
26
+ workerCount
27
+ designChangeCount
28
+ constructionCost
29
+ area
30
+ }
31
+ }
32
+ }
33
+ `,
34
+ variables: { id: projectId },
35
+ context: { headers: tenantHeaders(projectCode) }
36
+ });
37
+ if (response.errors) {
38
+ return {};
39
+ }
40
+ return response.data.project;
41
+ };
42
+ export const getKpiMetrics = async (projectCode) => {
43
+ const response = await client.query({
44
+ query: gql `
45
+ query KpiMetrics {
46
+ kpiMetrics {
47
+ items {
48
+ id
49
+ name
50
+ unit
51
+ periodType
52
+ description
53
+ active
54
+ rank
55
+ }
56
+ }
57
+ }
58
+ `,
59
+ context: { headers: tenantHeaders(projectCode) }
60
+ });
61
+ if (response.errors) {
62
+ return [];
63
+ }
64
+ // KpiMetric 자체의 active 만 통과 — admin 에서 active=false 로 비활성화한 metric 은 자동 제외.
65
+ // rank 오름차순 정렬 (미지정=null 은 뒤로). rank 가 같으면 기존(서버) 순서 유지.
66
+ const items = (response.data.kpiMetrics.items || []).filter((m) => m.active !== false);
67
+ return items
68
+ .map((m, i) => ({ m, i }))
69
+ .sort((a, b) => {
70
+ var _a, _b;
71
+ const ra = (_a = a.m.rank) !== null && _a !== void 0 ? _a : Number.POSITIVE_INFINITY;
72
+ const rb = (_b = b.m.rank) !== null && _b !== void 0 ? _b : Number.POSITIVE_INFINITY;
73
+ return ra !== rb ? ra - rb : a.i - b.i;
74
+ })
75
+ .map((x) => x.m);
76
+ };
77
+ export const getKpiMetricValues = async (projectId, projectCode) => {
78
+ const filters = [{ name: 'org', operator: 'eq', value: projectId }];
79
+ const response = await client.query({
80
+ query: gql `
81
+ query KpiMetricValues($filters: [Filter!], $projectId: String) {
82
+ kpiMetricValues(filters: $filters, projectId: $projectId) {
83
+ items {
84
+ id
85
+ value
86
+ unit
87
+ periodType
88
+ valueDate
89
+ metricId
90
+ }
91
+ }
92
+ }
93
+ `,
94
+ variables: {
95
+ filters,
96
+ // projectId 전달 → 서버가 그 프로젝트 도메인으로 스코프 → cross-domain 중복 row 방지.
97
+ projectId
98
+ },
99
+ context: { headers: tenantHeaders(projectCode) }
100
+ });
101
+ if (response.errors) {
102
+ return [];
103
+ }
104
+ return response.data.kpiMetricValues.items;
105
+ };
106
+ export const getKpiCategories = async (projectCode) => {
107
+ const response = await client.query({
108
+ query: gql `
109
+ query {
110
+ kpiCategories: kpisLevel1 {
111
+ id
112
+ name
113
+ description
114
+ active
115
+ kpis: children {
116
+ id
117
+ name
118
+ description
119
+ formula
120
+ active
121
+ grades
122
+ }
123
+ }
124
+ }
125
+ `,
126
+ context: { headers: tenantHeaders(projectCode) }
127
+ });
128
+ if (response.errors) {
129
+ return [];
130
+ }
131
+ return response.data.kpiCategories;
132
+ };
133
+ /**
134
+ * BuildingComplex 컬럼 부분 업데이트. updateBuildingComplex 는 기존 row 를 load 후
135
+ * patch 를 merge 저장하므로 (building-complex-mutation.ts) 지정 컬럼만 안전하게 갱신
136
+ * — updateProject 처럼 buildings 재생성 side-effect 가 없다.
137
+ */
138
+ export const updateBuildingComplexFields = async (id, patch, projectCode) => {
139
+ return await client.mutate({
140
+ mutation: gql `
141
+ mutation UpdateBuildingComplex($id: String!, $patch: BuildingComplexPatch!) {
142
+ updateBuildingComplex(id: $id, patch: $patch) {
143
+ id
144
+ }
145
+ }
146
+ `,
147
+ variables: { id, patch },
148
+ context: { headers: tenantHeaders(projectCode) }
149
+ });
150
+ };
151
+ export const updateProjectCompleteStep1 = async (patches, projectCode) => {
152
+ const response = await client.mutate({
153
+ mutation: gql `
154
+ mutation UpdateKpiMetricValuesCumulative($patches: [KpiMetricValuePatch!]!) {
155
+ updateKpiMetricValuesCumulative(patches: $patches) {
156
+ id
157
+ }
158
+ }
159
+ `,
160
+ variables: {
161
+ patches
162
+ },
163
+ context: { headers: tenantHeaders(projectCode) }
164
+ });
165
+ return response;
166
+ };
167
+ export const updateProjectCompleteStep2 = async (patches, projectCode) => {
168
+ const response = await client.mutate({
169
+ mutation: gql `
170
+ mutation UpdateKpiMetricValuesAssessment($patches: [KpiMetricValuePatch!]!) {
171
+ updateKpiMetricValuesAssessment(patches: $patches) {
172
+ id
173
+ }
174
+ }
175
+ `,
176
+ variables: {
177
+ patches
178
+ },
179
+ context: { headers: tenantHeaders(projectCode) }
180
+ });
181
+ return response;
182
+ };
183
+ export const updateProjectCompleteStep3 = async (file, projectId, projectCode) => {
184
+ const response = await client.mutate({
185
+ mutation: gql `
186
+ mutation UpdateKpiMetricValuesSentiment($attachment: NewAttachment!) {
187
+ updateKpiMetricValuesSentiment(attachment: $attachment) {
188
+ id
189
+ name
190
+ fullpath
191
+ }
192
+ }
193
+ `,
194
+ variables: {
195
+ attachment: {
196
+ file,
197
+ refBy: projectId,
198
+ refType: 'ProjectCompleteReport'
199
+ }
200
+ },
201
+ context: {
202
+ hasUpload: true,
203
+ headers: tenantHeaders(projectCode)
204
+ }
205
+ });
206
+ return response;
207
+ };
208
+ export const collectProjectExternalData = async (projectId, projectCode) => {
209
+ const response = await client.mutate({
210
+ mutation: gql `
211
+ mutation CollectProjectExternalData($projectId: String!) {
212
+ collectProjectExternalData(projectId: $projectId) {
213
+ source
214
+ label
215
+ ok
216
+ message
217
+ data
218
+ }
219
+ }
220
+ `,
221
+ variables: { projectId },
222
+ context: { headers: tenantHeaders(projectCode) }
223
+ });
224
+ if (response.errors) {
225
+ // 전체 mutation 자체 실패 — UI 가 시스템별 fallback 결과 구성
226
+ throw new Error(response.errors.map((e) => e.message).join('\n'));
227
+ }
228
+ return response.data.collectProjectExternalData;
229
+ };
230
+ export const updateProjectCompleteFinalize = async (projectId, projectCode) => {
231
+ const response = await client.mutate({
232
+ mutation: gql `
233
+ mutation FinalizeProjectWithKpiRecalculation($projectId: String!) {
234
+ finalizeProjectWithKpiRecalculation(projectId: $projectId)
235
+ }
236
+ `,
237
+ variables: {
238
+ projectId
239
+ },
240
+ context: { headers: tenantHeaders(projectCode) }
241
+ });
242
+ return response;
243
+ };
244
+ /** 완료(COMPLETED) 프로젝트를 진행중(ONGOING) 으로 되돌림 (완료처리 토글의 해제). */
245
+ export const updateProjectCompleteUnfinalize = async (projectId, projectCode) => {
246
+ const response = await client.mutate({
247
+ mutation: gql `
248
+ mutation UpdateProject($project: ProjectPatch!) {
249
+ updateProject(project: $project) {
250
+ id
251
+ state
252
+ }
253
+ }
254
+ `,
255
+ variables: { project: { id: projectId, state: 'ONGOING' } },
256
+ context: { headers: tenantHeaders(projectCode) }
257
+ });
258
+ return response;
259
+ };
260
+ export const recalculateProjectKpi = async (projectId, projectCode) => {
261
+ const response = await client.mutate({
262
+ mutation: gql `
263
+ mutation RecalculateProjectKpi($projectId: String!) {
264
+ recalculateProjectKpi(projectId: $projectId)
265
+ }
266
+ `,
267
+ variables: {
268
+ projectId
269
+ },
270
+ context: { headers: tenantHeaders(projectCode) }
271
+ });
272
+ return response;
273
+ };
274
+ //# sourceMappingURL=complete-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"complete-api.js","sourceRoot":"","sources":["../../client/shared/complete-api.ts"],"names":[],"mappings":"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","sourcesContent":["import { gql } from '@apollo/client'\nimport { client } from '@operato/graphql'\nimport { tenantHeaders } from '@dssp/project/dist-client/shared/domain-context'\n\nexport const getProject = async (projectId: string, projectCode?: string) => {\n const response = await client.query({\n query: gql`\n query Project($id: String!) {\n project(id: $id) {\n id\n code\n name\n startDate\n endDate\n state\n\n completeReport {\n id\n name\n fullpath\n }\n\n buildingComplex {\n id\n coverageRatio\n floorAreaRatio\n workerCount\n designChangeCount\n constructionCost\n area\n }\n }\n }\n `,\n variables: { id: projectId },\n context: { headers: tenantHeaders(projectCode) }\n })\n\n if (response.errors) {\n return {}\n }\n\n return response.data.project\n}\n\nexport const getKpiMetrics = async (projectCode?: string) => {\n const response = await client.query({\n query: gql`\n query KpiMetrics {\n kpiMetrics {\n items {\n id\n name\n unit\n periodType\n description\n active\n rank\n }\n }\n }\n `,\n context: { headers: tenantHeaders(projectCode) }\n })\n\n if (response.errors) {\n return []\n }\n\n // KpiMetric 자체의 active 만 통과 — admin 에서 active=false 로 비활성화한 metric 은 자동 제외.\n // rank 오름차순 정렬 (미지정=null 은 뒤로). rank 가 같으면 기존(서버) 순서 유지.\n const items = (response.data.kpiMetrics.items || []).filter((m: any) => m.active !== false)\n return items\n .map((m: any, i: number) => ({ m, i }))\n .sort((a: any, b: any) => {\n const ra = a.m.rank ?? Number.POSITIVE_INFINITY\n const rb = b.m.rank ?? Number.POSITIVE_INFINITY\n return ra !== rb ? ra - rb : a.i - b.i\n })\n .map((x: any) => x.m)\n}\n\nexport const getKpiMetricValues = async (projectId: string, projectCode?: string) => {\n const filters = [{ name: 'org', operator: 'eq', value: projectId }]\n\n const response = await client.query({\n query: gql`\n query KpiMetricValues($filters: [Filter!], $projectId: String) {\n kpiMetricValues(filters: $filters, projectId: $projectId) {\n items {\n id\n value\n unit\n periodType\n valueDate\n metricId\n }\n }\n }\n `,\n variables: {\n filters,\n // projectId 전달 → 서버가 그 프로젝트 도메인으로 스코프 → cross-domain 중복 row 방지.\n projectId\n },\n context: { headers: tenantHeaders(projectCode) }\n })\n\n if (response.errors) {\n return []\n }\n\n return response.data.kpiMetricValues.items\n}\n\nexport const getKpiCategories = async (projectCode?: string) => {\n const response = await client.query({\n query: gql`\n query {\n kpiCategories: kpisLevel1 {\n id\n name\n description\n active\n kpis: children {\n id\n name\n description\n formula\n active\n grades\n }\n }\n }\n `,\n context: { headers: tenantHeaders(projectCode) }\n })\n\n if (response.errors) {\n return []\n }\n\n return response.data.kpiCategories\n}\n\n/**\n * BuildingComplex 컬럼 부분 업데이트. updateBuildingComplex 는 기존 row 를 load 후\n * patch 를 merge 저장하므로 (building-complex-mutation.ts) 지정 컬럼만 안전하게 갱신\n * — updateProject 처럼 buildings 재생성 side-effect 가 없다.\n */\nexport const updateBuildingComplexFields = async (id: string, patch: any, projectCode?: string) => {\n return await client.mutate({\n mutation: gql`\n mutation UpdateBuildingComplex($id: String!, $patch: BuildingComplexPatch!) {\n updateBuildingComplex(id: $id, patch: $patch) {\n id\n }\n }\n `,\n variables: { id, patch },\n context: { headers: tenantHeaders(projectCode) }\n })\n}\n\nexport const updateProjectCompleteStep1 = async (patches: any, projectCode?: string) => {\n const response = await client.mutate({\n mutation: gql`\n mutation UpdateKpiMetricValuesCumulative($patches: [KpiMetricValuePatch!]!) {\n updateKpiMetricValuesCumulative(patches: $patches) {\n id\n }\n }\n `,\n variables: {\n patches\n },\n context: { headers: tenantHeaders(projectCode) }\n })\n\n return response\n}\n\nexport const updateProjectCompleteStep2 = async (patches: any, projectCode?: string) => {\n const response = await client.mutate({\n mutation: gql`\n mutation UpdateKpiMetricValuesAssessment($patches: [KpiMetricValuePatch!]!) {\n updateKpiMetricValuesAssessment(patches: $patches) {\n id\n }\n }\n `,\n variables: {\n patches\n },\n context: { headers: tenantHeaders(projectCode) }\n })\n\n return response\n}\n\nexport const updateProjectCompleteStep3 = async (file: any, projectId: string, projectCode?: string) => {\n const response = await client.mutate({\n mutation: gql`\n mutation UpdateKpiMetricValuesSentiment($attachment: NewAttachment!) {\n updateKpiMetricValuesSentiment(attachment: $attachment) {\n id\n name\n fullpath\n }\n }\n `,\n variables: {\n attachment: {\n file,\n refBy: projectId,\n refType: 'ProjectCompleteReport'\n }\n },\n context: {\n hasUpload: true,\n headers: tenantHeaders(projectCode)\n }\n })\n\n return response\n}\n\n/**\n * UI \"자동 수집\" 버튼 전용 — 세움터/올바로/키스콘 시나리오를 순차 호출 후\n * 시스템별 성공/실패 결과를 반환. 시나리오는 도메인 EnvVar 만으로 동작하므로\n * 파라미터 없음. 시나리오 내부 step 이 KPI Value 를 DB 에 적재하므로 호출 후\n * KPI Metric Values 를 다시 조회해 form 을 갱신하는 것이 정상 흐름.\n */\nexport interface ExternalDataCollectionResult {\n source: string // 'saeumteo' | 'allbaro' | 'kiscon'\n label: string // '세움터' | '올바로' | '키스콘'\n ok: boolean\n message: string\n /**\n * 시나리오 result 객체. projectKey: value 맵.\n * - 세움터: { area, floorAreaRatio, upperFloorCount, lowerFloorCount,\n * structureType, siteType }\n * - 올바로: { totalConstructionWasteAmount }\n * - 키스콘: { constructionPeriod, constructionCost }\n *\n * siteType (건물 용도, SiteType enum 값 — 예: APARTMENT_COMPLEX) 와\n * structureType (구조형태) 는 KPI metric 이 아니라 BuildingComplex 의 기본\n * 속성으로 사용. KISCON 의 \"공사유형(신설/증축)\" 은 SiteType 도메인과 다르므로\n * 매핑 대상 아님.\n *\n * 없는 키는 omit. 시나리오 실패 시 null/undefined.\n */\n data?: Record<string, number | string | null>\n}\n\nexport const collectProjectExternalData = async (\n projectId: string,\n projectCode?: string\n): Promise<ExternalDataCollectionResult[]> => {\n const response = await client.mutate({\n mutation: gql`\n mutation CollectProjectExternalData($projectId: String!) {\n collectProjectExternalData(projectId: $projectId) {\n source\n label\n ok\n message\n data\n }\n }\n `,\n variables: { projectId },\n context: { headers: tenantHeaders(projectCode) }\n })\n\n if (response.errors) {\n // 전체 mutation 자체 실패 — UI 가 시스템별 fallback 결과 구성\n throw new Error(response.errors.map((e: any) => e.message).join('\\n'))\n }\n\n return response.data.collectProjectExternalData\n}\n\nexport const updateProjectCompleteFinalize = async (projectId: string, projectCode?: string) => {\n const response = await client.mutate({\n mutation: gql`\n mutation FinalizeProjectWithKpiRecalculation($projectId: String!) {\n finalizeProjectWithKpiRecalculation(projectId: $projectId)\n }\n `,\n variables: {\n projectId\n },\n context: { headers: tenantHeaders(projectCode) }\n })\n\n return response\n}\n\n/** 완료(COMPLETED) 프로젝트를 진행중(ONGOING) 으로 되돌림 (완료처리 토글의 해제). */\nexport const updateProjectCompleteUnfinalize = async (projectId: string, projectCode?: string) => {\n const response = await client.mutate({\n mutation: gql`\n mutation UpdateProject($project: ProjectPatch!) {\n updateProject(project: $project) {\n id\n state\n }\n }\n `,\n variables: { project: { id: projectId, state: 'ONGOING' } },\n context: { headers: tenantHeaders(projectCode) }\n })\n\n return response\n}\n\nexport const recalculateProjectKpi = async (projectId: string, projectCode?: string) => {\n const response = await client.mutate({\n mutation: gql`\n mutation RecalculateProjectKpi($projectId: String!) {\n recalculateProjectKpi(projectId: $projectId)\n }\n `,\n variables: {\n projectId\n },\n context: { headers: tenantHeaders(projectCode) }\n })\n\n return response\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export declare const calcDiff: (a: string | number, b: string | number) => number;
2
+ export declare const calcDateDiff: (a: string, b: string) => number;
@@ -0,0 +1,22 @@
1
+ import moment from 'moment';
2
+ // a와 b의 차이를 계산하여 값과 클래스를 반환
3
+ export const calcDiff = (a, b) => {
4
+ const origin = Number(a || '0');
5
+ const complete = Number(b || '0');
6
+ const diff = complete - origin;
7
+ if (!isFinite(diff))
8
+ return 0;
9
+ return diff;
10
+ };
11
+ // a와 b의 날짜 차이를 계산하여 값과 클래스를 반환
12
+ export const calcDateDiff = (a, b) => {
13
+ const origin = moment(a, 'YYYY-MM-DD', true);
14
+ const complete = moment(b, 'YYYY-MM-DD', true);
15
+ if (!origin.isValid() || !complete.isValid())
16
+ return 0;
17
+ const diffDays = complete.diff(origin, 'days');
18
+ if (diffDays === 0)
19
+ return 0;
20
+ return diffDays;
21
+ };
22
+ //# sourceMappingURL=func.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"func.js","sourceRoot":"","sources":["../../client/shared/func.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAA;AAE3B,4BAA4B;AAC5B,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,CAAkB,EAAE,CAAkB,EAAU,EAAE;IACzE,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,IAAI,GAAG,CAAC,CAAA;IAC/B,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,IAAI,GAAG,CAAC,CAAA;IACjC,MAAM,IAAI,GAAG,QAAQ,GAAG,MAAM,CAAA;IAC9B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,OAAO,CAAC,CAAA;IAC7B,OAAO,IAAI,CAAA;AACb,CAAC,CAAA;AAED,+BAA+B;AAC/B,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAS,EAAE,CAAS,EAAU,EAAE;IAC3D,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,EAAE,YAAY,EAAE,IAAI,CAAC,CAAA;IAC5C,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,EAAE,YAAY,EAAE,IAAI,CAAC,CAAA;IAC9C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE;QAAE,OAAO,CAAC,CAAA;IAEtD,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC9C,IAAI,QAAQ,KAAK,CAAC;QAAE,OAAO,CAAC,CAAA;IAE5B,OAAO,QAAQ,CAAA;AACjB,CAAC,CAAA","sourcesContent":["import moment from 'moment'\n\n// a와 b의 차이를 계산하여 값과 클래스를 반환\nexport const calcDiff = (a: string | number, b: string | number): number => {\n const origin = Number(a || '0')\n const complete = Number(b || '0')\n const diff = complete - origin\n if (!isFinite(diff)) return 0\n return diff\n}\n\n// a와 b의 날짜 차이를 계산하여 값과 클래스를 반환\nexport const calcDateDiff = (a: string, b: string): number => {\n const origin = moment(a, 'YYYY-MM-DD', true)\n const complete = moment(b, 'YYYY-MM-DD', true)\n if (!origin.isValid() || !complete.isValid()) return 0\n\n const diffDays = complete.diff(origin, 'days')\n if (diffDays === 0) return 0\n\n return diffDays\n}\n"]}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * 완공 입력 화면의 "자동 수집" 워크플로용 외부 시스템 연동 수집 함수.
3
+ *
4
+ * 데모 목적: 키스콘·세움터·올바로에서 "연동을 통해 값을 가져온다"는 워크플로를
5
+ * 보여주기 위한 것. 실제 스크래핑/API 연동의 깊이는 이 함수 내부에 캡슐화되어
6
+ * 있으므로, 추후 실연동(integration-kiscon / data-go-kr / integration-allbaro)
7
+ * 으로 교체할 때 이 본문만 바꾸면 화면/저장 흐름은 그대로 유지된다.
8
+ */
9
+ export type IntegrationSource = 'kiscon' | 'saeumteo' | 'allbaro';
10
+ export interface IntegrationSourceMeta {
11
+ source: IntegrationSource;
12
+ label: string;
13
+ icon: string;
14
+ desc: string;
15
+ }
16
+ /** 화면에 표시할 연동 소스 목록 (수집 순서대로) */
17
+ export declare const INTEGRATION_SOURCES: IntegrationSourceMeta[];
18
+ export interface CollectedField {
19
+ /** 폼의 projectKey (KPI_METRIC_KEY_MAPPING 의 projectKey 와 일치) */
20
+ projectKey: string;
21
+ label: string;
22
+ value: number | string;
23
+ unit?: string;
24
+ }
25
+ export interface CollectionResult {
26
+ source: IntegrationSource;
27
+ label: string;
28
+ fields: CollectedField[];
29
+ }
30
+ /**
31
+ * 한 외부 시스템에서 연동으로 값을 수집한다.
32
+ * 데모에서는 연동 수집을 흉내내기 위해 짧은 지연 후 대표값을 반환한다
33
+ * (프로젝트에 이미 있는 값이 있으면 우선 사용).
34
+ */
35
+ export declare function collectFromSource(source: IntegrationSource, project: any): Promise<CollectionResult>;