@dssp/dkpi 1.0.0 → 1.0.1

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 (236) hide show
  1. package/dist-client/bootstrap.d.ts +9 -0
  2. package/dist-client/bootstrap.js +272 -0
  3. package/dist-client/bootstrap.js.map +1 -0
  4. package/dist-client/components/kpi-2d-lookup-chart.d.ts +50 -0
  5. package/dist-client/components/kpi-2d-lookup-chart.js +263 -0
  6. package/dist-client/components/kpi-2d-lookup-chart.js.map +1 -0
  7. package/dist-client/components/kpi-boxplot-chart.d.ts +31 -0
  8. package/dist-client/components/kpi-boxplot-chart.js +306 -0
  9. package/dist-client/components/kpi-boxplot-chart.js.map +1 -0
  10. package/dist-client/components/kpi-lookup-chart.d.ts +58 -0
  11. package/dist-client/components/kpi-lookup-chart.js +464 -0
  12. package/dist-client/components/kpi-lookup-chart.js.map +1 -0
  13. package/dist-client/components/kpi-mini-trend-chart.d.ts +14 -0
  14. package/dist-client/components/kpi-mini-trend-chart.js +180 -0
  15. package/dist-client/components/kpi-mini-trend-chart.js.map +1 -0
  16. package/dist-client/components/kpi-radar-chart.d.ts +17 -0
  17. package/dist-client/components/kpi-radar-chart.js +259 -0
  18. package/dist-client/components/kpi-radar-chart.js.map +1 -0
  19. package/dist-client/components/kpi-single-boxplot-chart.d.ts +36 -0
  20. package/dist-client/components/kpi-single-boxplot-chart.js +417 -0
  21. package/dist-client/components/kpi-single-boxplot-chart.js.map +1 -0
  22. package/dist-client/components/kpi-step-lookup-chart.d.ts +33 -0
  23. package/dist-client/components/kpi-step-lookup-chart.js +181 -0
  24. package/dist-client/components/kpi-step-lookup-chart.js.map +1 -0
  25. package/dist-client/components/kpi-trend-chart.d.ts +25 -0
  26. package/dist-client/components/kpi-trend-chart.js +241 -0
  27. package/dist-client/components/kpi-trend-chart.js.map +1 -0
  28. package/dist-client/components/sv-pagenation-control.d.ts +18 -0
  29. package/dist-client/components/sv-pagenation-control.js +142 -0
  30. package/dist-client/components/sv-pagenation-control.js.map +1 -0
  31. package/dist-client/entries/auth/checkin.d.ts +38 -0
  32. package/dist-client/entries/auth/checkin.js +551 -0
  33. package/dist-client/entries/auth/checkin.js.map +1 -0
  34. package/dist-client/entries/public/home.d.ts +8 -0
  35. package/dist-client/entries/public/home.js +97 -0
  36. package/dist-client/entries/public/home.js.map +1 -0
  37. package/dist-client/google-map/common-google-map.d.ts +35 -0
  38. package/dist-client/google-map/common-google-map.js +349 -0
  39. package/dist-client/google-map/common-google-map.js.map +1 -0
  40. package/dist-client/google-map/google-map-loader.d.ts +6 -0
  41. package/dist-client/google-map/google-map-loader.js +23 -0
  42. package/dist-client/google-map/google-map-loader.js.map +1 -0
  43. package/dist-client/icons/menu-icons.d.ts +11 -0
  44. package/dist-client/icons/menu-icons.js +126 -0
  45. package/dist-client/icons/menu-icons.js.map +1 -0
  46. package/dist-client/index.d.ts +0 -0
  47. package/dist-client/index.js +2 -0
  48. package/dist-client/index.js.map +1 -0
  49. package/dist-client/menu.d.ts +23 -0
  50. package/dist-client/menu.js +59 -0
  51. package/dist-client/menu.js.map +1 -0
  52. package/dist-client/pages/component/project-update-header.d.ts +1 -0
  53. package/dist-client/pages/component/project-update-header.js +127 -0
  54. package/dist-client/pages/component/project-update-header.js.map +1 -0
  55. package/dist-client/pages/kpi-admin/dssp-kpi-list-page.d.ts +22 -0
  56. package/dist-client/pages/kpi-admin/dssp-kpi-list-page.js +57 -0
  57. package/dist-client/pages/kpi-admin/dssp-kpi-list-page.js.map +1 -0
  58. package/dist-client/pages/kpi-admin/dssp-kpi-overview.d.ts +47 -0
  59. package/dist-client/pages/kpi-admin/dssp-kpi-overview.js +393 -0
  60. package/dist-client/pages/kpi-admin/dssp-kpi-overview.js.map +1 -0
  61. package/dist-client/pages/kpi-admin/kpi-grade-2d-editor.d.ts +20 -0
  62. package/dist-client/pages/kpi-admin/kpi-grade-2d-editor.js +445 -0
  63. package/dist-client/pages/kpi-admin/kpi-grade-2d-editor.js.map +1 -0
  64. package/dist-client/pages/kpi-admin/kpi-system-guide.d.ts +18 -0
  65. package/dist-client/pages/kpi-admin/kpi-system-guide.js +696 -0
  66. package/dist-client/pages/kpi-admin/kpi-system-guide.js.map +1 -0
  67. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.d.ts +18 -0
  68. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js +259 -0
  69. package/dist-client/pages/kpi-dashboard/cards/kpi-level1-card.js.map +1 -0
  70. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.d.ts +22 -0
  71. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js +346 -0
  72. package/dist-client/pages/kpi-dashboard/cards/kpi-level2-comparison.js.map +1 -0
  73. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.d.ts +26 -0
  74. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js +433 -0
  75. package/dist-client/pages/kpi-dashboard/cards/kpi-level3-comparison.js.map +1 -0
  76. package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.d.ts +8 -0
  77. package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.js +78 -0
  78. package/dist-client/pages/kpi-dashboard/components/kpi-chart-toggle.js.map +1 -0
  79. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.d.ts +46 -0
  80. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js +945 -0
  81. package/dist-client/pages/kpi-dashboard/components/kpi-left-panel.js.map +1 -0
  82. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.d.ts +34 -0
  83. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js +238 -0
  84. package/dist-client/pages/kpi-dashboard/components/kpi-map-panel.js.map +1 -0
  85. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.d.ts +37 -0
  86. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js +689 -0
  87. package/dist-client/pages/kpi-dashboard/components/kpi-region-popup.js.map +1 -0
  88. package/dist-client/pages/kpi-dashboard/kpi-alert-panel.d.ts +18 -0
  89. package/dist-client/pages/kpi-dashboard/kpi-alert-panel.js +131 -0
  90. package/dist-client/pages/kpi-dashboard/kpi-alert-panel.js.map +1 -0
  91. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.d.ts +57 -0
  92. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js +866 -0
  93. package/dist-client/pages/kpi-dashboard/kpi-dashboard-map.js.map +1 -0
  94. package/dist-client/pages/kpi-dashboard/kpi-dashboard.d.ts +63 -0
  95. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js +1097 -0
  96. package/dist-client/pages/kpi-dashboard/kpi-dashboard.js.map +1 -0
  97. package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.d.ts +12 -0
  98. package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.js +82 -0
  99. package/dist-client/pages/kpi-dashboard/kpi-grade-visualization.js.map +1 -0
  100. package/dist-client/pages/kpi-dashboard/kpi-history-viewer.d.ts +11 -0
  101. package/dist-client/pages/kpi-dashboard/kpi-history-viewer.js +65 -0
  102. package/dist-client/pages/kpi-dashboard/kpi-history-viewer.js.map +1 -0
  103. package/dist-client/pages/kpi-dashboard/kpi-list-summary.d.ts +13 -0
  104. package/dist-client/pages/kpi-dashboard/kpi-list-summary.js +115 -0
  105. package/dist-client/pages/kpi-dashboard/kpi-list-summary.js.map +1 -0
  106. package/dist-client/pages/kpi-dashboard/kpi-performance-summary.d.ts +15 -0
  107. package/dist-client/pages/kpi-dashboard/kpi-performance-summary.js +147 -0
  108. package/dist-client/pages/kpi-dashboard/kpi-performance-summary.js.map +1 -0
  109. package/dist-client/pages/kpi-dashboard/kpi-value-entry.d.ts +7 -0
  110. package/dist-client/pages/kpi-dashboard/kpi-value-entry.js +86 -0
  111. package/dist-client/pages/kpi-dashboard/kpi-value-entry.js.map +1 -0
  112. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.d.ts +57 -0
  113. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js +719 -0
  114. package/dist-client/pages/kpi-metric-value/kpi-metric-value-editor-page.js.map +1 -0
  115. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.d.ts +23 -0
  116. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js +76 -0
  117. package/dist-client/pages/kpi-metric-value/kpi-metric-value-importer.js.map +1 -0
  118. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.d.ts +68 -0
  119. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js +394 -0
  120. package/dist-client/pages/kpi-metric-value/kpi-metric-value-list-page.js.map +1 -0
  121. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.d.ts +12 -0
  122. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js +174 -0
  123. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-form.js.map +1 -0
  124. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.d.ts +40 -0
  125. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js +190 -0
  126. package/dist-client/pages/kpi-metric-value/kpi-metric-value-manual-entry-page.js.map +1 -0
  127. package/dist-client/pages/kpi-value/kpi-value-importer.d.ts +23 -0
  128. package/dist-client/pages/kpi-value/kpi-value-importer.js +93 -0
  129. package/dist-client/pages/kpi-value/kpi-value-importer.js.map +1 -0
  130. package/dist-client/pages/kpi-value/kpi-value-list-page.d.ts +71 -0
  131. package/dist-client/pages/kpi-value/kpi-value-list-page.js +454 -0
  132. package/dist-client/pages/kpi-value/kpi-value-list-page.js.map +1 -0
  133. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.d.ts +69 -0
  134. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js +790 -0
  135. package/dist-client/pages/project-complete-tabs/pc-tab1-plan.js.map +1 -0
  136. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.d.ts +22 -0
  137. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js +344 -0
  138. package/dist-client/pages/project-complete-tabs/pc-tab2-rating.js.map +1 -0
  139. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.d.ts +21 -0
  140. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.js +335 -0
  141. package/dist-client/pages/project-complete-tabs/pc-tab3-upload.js.map +1 -0
  142. package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.d.ts +51 -0
  143. package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.js +693 -0
  144. package/dist-client/pages/project-complete-tabs/pc-tab4-monthly.js.map +1 -0
  145. package/dist-client/pages/sv-project-complete.d.ts +31 -0
  146. package/dist-client/pages/sv-project-complete.js +359 -0
  147. package/dist-client/pages/sv-project-complete.js.map +1 -0
  148. package/dist-client/pages/sv-project-completed-list.d.ts +27 -0
  149. package/dist-client/pages/sv-project-completed-list.js +416 -0
  150. package/dist-client/pages/sv-project-completed-list.js.map +1 -0
  151. package/dist-client/pages/sv-project-detail.d.ts +73 -0
  152. package/dist-client/pages/sv-project-detail.js +1501 -0
  153. package/dist-client/pages/sv-project-detail.js.map +1 -0
  154. package/dist-client/pages/sv-project-list.d.ts +175 -0
  155. package/dist-client/pages/sv-project-list.js +581 -0
  156. package/dist-client/pages/sv-project-list.js.map +1 -0
  157. package/dist-client/pages/sv-project-update.d.ts +91 -0
  158. package/dist-client/pages/sv-project-update.js +1150 -0
  159. package/dist-client/pages/sv-project-update.js.map +1 -0
  160. package/dist-client/pages/sv-user-management.d.ts +6 -0
  161. package/dist-client/pages/sv-user-management.js +224 -0
  162. package/dist-client/pages/sv-user-management.js.map +1 -0
  163. package/dist-client/route.d.ts +1 -0
  164. package/dist-client/route.js +58 -0
  165. package/dist-client/route.js.map +1 -0
  166. package/dist-client/shared/complete-api.d.ts +45 -0
  167. package/dist-client/shared/complete-api.js +274 -0
  168. package/dist-client/shared/complete-api.js.map +1 -0
  169. package/dist-client/shared/func.d.ts +2 -0
  170. package/dist-client/shared/func.js +22 -0
  171. package/dist-client/shared/func.js.map +1 -0
  172. package/dist-client/shared/geo-group-mapping.d.ts +25 -0
  173. package/dist-client/shared/geo-group-mapping.js +189 -0
  174. package/dist-client/shared/geo-group-mapping.js.map +1 -0
  175. package/dist-client/shared/integration-fetch.d.ts +35 -0
  176. package/dist-client/shared/integration-fetch.js +53 -0
  177. package/dist-client/shared/integration-fetch.js.map +1 -0
  178. package/dist-client/shared/kpi-project-sync.d.ts +16 -0
  179. package/dist-client/shared/kpi-project-sync.js +129 -0
  180. package/dist-client/shared/kpi-project-sync.js.map +1 -0
  181. package/dist-client/themes/dark.css +51 -0
  182. package/dist-client/themes/light.css +51 -0
  183. package/dist-client/tsconfig.tsbuildinfo +1 -0
  184. package/dist-client/viewparts/menu-tools.d.ts +62 -0
  185. package/dist-client/viewparts/menu-tools.js +554 -0
  186. package/dist-client/viewparts/menu-tools.js.map +1 -0
  187. package/dist-client/viewparts/user-circle.d.ts +5 -0
  188. package/dist-client/viewparts/user-circle.js +26 -0
  189. package/dist-client/viewparts/user-circle.js.map +1 -0
  190. package/dist-server/index.d.ts +2 -0
  191. package/dist-server/index.js +6 -0
  192. package/dist-server/index.js.map +1 -0
  193. package/dist-server/migrations/index.d.ts +1 -0
  194. package/dist-server/migrations/index.js +12 -0
  195. package/dist-server/migrations/index.js.map +1 -0
  196. package/dist-server/scripts/calculate-kpi-scores.d.ts +10 -0
  197. package/dist-server/scripts/calculate-kpi-scores.js +333 -0
  198. package/dist-server/scripts/calculate-kpi-scores.js.map +1 -0
  199. package/dist-server/scripts/load-grade-data-migration.d.ts +14 -0
  200. package/dist-server/scripts/load-grade-data-migration.js +279 -0
  201. package/dist-server/scripts/load-grade-data-migration.js.map +1 -0
  202. package/dist-server/scripts/propagate-parent-kpi-values.d.ts +14 -0
  203. package/dist-server/scripts/propagate-parent-kpi-values.js +786 -0
  204. package/dist-server/scripts/propagate-parent-kpi-values.js.map +1 -0
  205. package/dist-server/scripts/recalculate-by-project-name.d.ts +2 -0
  206. package/dist-server/scripts/recalculate-by-project-name.js +72 -0
  207. package/dist-server/scripts/recalculate-by-project-name.js.map +1 -0
  208. package/dist-server/service/index.d.ts +4 -0
  209. package/dist-server/service/index.js +20 -0
  210. package/dist-server/service/index.js.map +1 -0
  211. package/dist-server/service/kpi-metric-value/index.d.ts +4 -0
  212. package/dist-server/service/kpi-metric-value/index.js +8 -0
  213. package/dist-server/service/kpi-metric-value/index.js.map +1 -0
  214. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.d.ts +198 -0
  215. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js +1067 -0
  216. package/dist-server/service/kpi-metric-value/kpi-metric-value-mutation.js.map +1 -0
  217. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.d.ts +15 -0
  218. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js +72 -0
  219. package/dist-server/service/kpi-metric-value/kpi-metric-value-query.js.map +1 -0
  220. package/dist-server/service/kpi-stat/index.d.ts +4 -0
  221. package/dist-server/service/kpi-stat/index.js +8 -0
  222. package/dist-server/service/kpi-stat/index.js.map +1 -0
  223. package/dist-server/service/kpi-stat/kpi-stat-query.d.ts +13 -0
  224. package/dist-server/service/kpi-stat/kpi-stat-query.js +751 -0
  225. package/dist-server/service/kpi-stat/kpi-stat-query.js.map +1 -0
  226. package/dist-server/service/kpi-stat/kpi-stat-types.d.ts +32 -0
  227. package/dist-server/service/kpi-stat/kpi-stat-types.js +180 -0
  228. package/dist-server/service/kpi-stat/kpi-stat-types.js.map +1 -0
  229. package/dist-server/service/kpi-value/index.d.ts +3 -0
  230. package/dist-server/service/kpi-value/index.js +7 -0
  231. package/dist-server/service/kpi-value/index.js.map +1 -0
  232. package/dist-server/service/kpi-value/kpi-value-query.d.ts +8 -0
  233. package/dist-server/service/kpi-value/kpi-value-query.js +90 -0
  234. package/dist-server/service/kpi-value/kpi-value-query.js.map +1 -0
  235. package/dist-server/tsconfig.tsbuildinfo +1 -0
  236. package/package.json +5 -5
@@ -0,0 +1,53 @@
1
+ /**
2
+ * 완공 입력 화면의 "자동 수집" 워크플로용 외부 시스템 연동 수집 함수.
3
+ *
4
+ * 데모 목적: 키스콘·세움터·올바로에서 "연동을 통해 값을 가져온다"는 워크플로를
5
+ * 보여주기 위한 것. 실제 스크래핑/API 연동의 깊이는 이 함수 내부에 캡슐화되어
6
+ * 있으므로, 추후 실연동(integration-kiscon / data-go-kr / integration-allbaro)
7
+ * 으로 교체할 때 이 본문만 바꾸면 화면/저장 흐름은 그대로 유지된다.
8
+ */
9
+ /** 화면에 표시할 연동 소스 목록 (수집 순서대로) */
10
+ export const INTEGRATION_SOURCES = [
11
+ { source: 'kiscon', label: '키스콘', icon: '🏗', desc: '건설공사대장 통보' },
12
+ { source: 'saeumteo', label: '세움터', icon: '🏛', desc: '건축물대장 / 건축HUB' },
13
+ { source: 'allbaro', label: '올바로', icon: '♻', desc: '건설폐기물 처리실적' }
14
+ ];
15
+ /**
16
+ * 한 외부 시스템에서 연동으로 값을 수집한다.
17
+ * 데모에서는 연동 수집을 흉내내기 위해 짧은 지연 후 대표값을 반환한다
18
+ * (프로젝트에 이미 있는 값이 있으면 우선 사용).
19
+ */
20
+ export async function collectFromSource(source, project) {
21
+ // 연동 수집이 "진행되는" 느낌을 주기 위한 짧은 지연 (데모용)
22
+ await new Promise(r => setTimeout(r, 700));
23
+ const bc = (project === null || project === void 0 ? void 0 : project.buildingComplex) || {};
24
+ switch (source) {
25
+ case 'kiscon':
26
+ return {
27
+ source,
28
+ label: '키스콘',
29
+ fields: [
30
+ { projectKey: 'constructionPeriod', label: '계획공사기간', value: 30, unit: '개월' },
31
+ { projectKey: 'constructionCost', label: '계획공사비', value: bc.constructionCost || 2500, unit: '억원' }
32
+ ]
33
+ };
34
+ case 'saeumteo':
35
+ return {
36
+ source,
37
+ label: '세움터',
38
+ fields: [
39
+ { projectKey: 'area', label: '연면적', value: bc.area || 114000, unit: '㎡' },
40
+ { projectKey: 'floorAreaRatio', label: '용적률', value: bc.floorAreaRatio || 250, unit: '%' },
41
+ { projectKey: 'upperFloorCount', label: '지상층수', value: 25, unit: '층' },
42
+ { projectKey: 'lowerFloorCount', label: '지하층수', value: 3, unit: '층' }
43
+ ]
44
+ };
45
+ case 'allbaro':
46
+ return {
47
+ source,
48
+ label: '올바로',
49
+ fields: [{ projectKey: 'totalConstructionWasteAmount', label: '총 건설폐기물', value: 15, unit: 'ton' }]
50
+ };
51
+ }
52
+ }
53
+ //# sourceMappingURL=integration-fetch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integration-fetch.js","sourceRoot":"","sources":["../../client/shared/integration-fetch.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAWH,iCAAiC;AACjC,MAAM,CAAC,MAAM,mBAAmB,GAA4B;IAC1D,EAAE,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE;IACjE,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,eAAe,EAAE;IACvE,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,YAAY,EAAE;CACnE,CAAA;AAgBD;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,MAAyB,EAAE,OAAY;IAC7E,sCAAsC;IACtC,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAA;IAE1C,MAAM,EAAE,GAAG,CAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,eAAe,KAAI,EAAE,CAAA;IAEzC,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,QAAQ;YACX,OAAO;gBACL,MAAM;gBACN,KAAK,EAAE,KAAK;gBACZ,MAAM,EAAE;oBACN,EAAE,UAAU,EAAE,oBAAoB,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE;oBAC5E,EAAE,UAAU,EAAE,kBAAkB,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,gBAAgB,IAAI,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE;iBACnG;aACF,CAAA;QAEH,KAAK,UAAU;YACb,OAAO;gBACL,MAAM;gBACN,KAAK,EAAE,KAAK;gBACZ,MAAM,EAAE;oBACN,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,IAAI,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE;oBACzE,EAAE,UAAU,EAAE,gBAAgB,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,cAAc,IAAI,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE;oBAC1F,EAAE,UAAU,EAAE,iBAAiB,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE;oBACtE,EAAE,UAAU,EAAE,iBAAiB,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE;iBACtE;aACF,CAAA;QAEH,KAAK,SAAS;YACZ,OAAO;gBACL,MAAM;gBACN,KAAK,EAAE,KAAK;gBACZ,MAAM,EAAE,CAAC,EAAE,UAAU,EAAE,8BAA8B,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;aACnG,CAAA;IACL,CAAC;AACH,CAAC","sourcesContent":["/**\n * 완공 입력 화면의 \"자동 수집\" 워크플로용 외부 시스템 연동 수집 함수.\n *\n * 데모 목적: 키스콘·세움터·올바로에서 \"연동을 통해 값을 가져온다\"는 워크플로를\n * 보여주기 위한 것. 실제 스크래핑/API 연동의 깊이는 이 함수 내부에 캡슐화되어\n * 있으므로, 추후 실연동(integration-kiscon / data-go-kr / integration-allbaro)\n * 으로 교체할 때 이 본문만 바꾸면 화면/저장 흐름은 그대로 유지된다.\n */\n\nexport type IntegrationSource = 'kiscon' | 'saeumteo' | 'allbaro'\n\nexport interface IntegrationSourceMeta {\n source: IntegrationSource\n label: string\n icon: string\n desc: string\n}\n\n/** 화면에 표시할 연동 소스 목록 (수집 순서대로) */\nexport const INTEGRATION_SOURCES: IntegrationSourceMeta[] = [\n { source: 'kiscon', label: '키스콘', icon: '🏗', desc: '건설공사대장 통보' },\n { source: 'saeumteo', label: '세움터', icon: '🏛', desc: '건축물대장 / 건축HUB' },\n { source: 'allbaro', label: '올바로', icon: '♻', desc: '건설폐기물 처리실적' }\n]\n\nexport interface CollectedField {\n /** 폼의 projectKey (KPI_METRIC_KEY_MAPPING 의 projectKey 와 일치) */\n projectKey: string\n label: string\n value: number | string\n unit?: string\n}\n\nexport interface CollectionResult {\n source: IntegrationSource\n label: string\n fields: CollectedField[]\n}\n\n/**\n * 한 외부 시스템에서 연동으로 값을 수집한다.\n * 데모에서는 연동 수집을 흉내내기 위해 짧은 지연 후 대표값을 반환한다\n * (프로젝트에 이미 있는 값이 있으면 우선 사용).\n */\nexport async function collectFromSource(source: IntegrationSource, project: any): Promise<CollectionResult> {\n // 연동 수집이 \"진행되는\" 느낌을 주기 위한 짧은 지연 (데모용)\n await new Promise(r => setTimeout(r, 700))\n\n const bc = project?.buildingComplex || {}\n\n switch (source) {\n case 'kiscon':\n return {\n source,\n label: '키스콘',\n fields: [\n { projectKey: 'constructionPeriod', label: '계획공사기간', value: 30, unit: '개월' },\n { projectKey: 'constructionCost', label: '계획공사비', value: bc.constructionCost || 2500, unit: '억원' }\n ]\n }\n\n case 'saeumteo':\n return {\n source,\n label: '세움터',\n fields: [\n { projectKey: 'area', label: '연면적', value: bc.area || 114000, unit: '㎡' },\n { projectKey: 'floorAreaRatio', label: '용적률', value: bc.floorAreaRatio || 250, unit: '%' },\n { projectKey: 'upperFloorCount', label: '지상층수', value: 25, unit: '층' },\n { projectKey: 'lowerFloorCount', label: '지하층수', value: 3, unit: '층' }\n ]\n }\n\n case 'allbaro':\n return {\n source,\n label: '올바로',\n fields: [{ projectKey: 'totalConstructionWasteAmount', label: '총 건설폐기물', value: 15, unit: 'ton' }]\n }\n }\n}\n"]}
@@ -0,0 +1,16 @@
1
+ /** BuildingComplex 실컬럼 보유 → 프로젝트정보 ↔ KPI 양방향 동기 대상.
2
+ * (constructionCost 는 억원 소수 가능 → 정수화 대상 아님) */
3
+ export declare const BIDIRECTIONAL_BC_KEYS: readonly ["workerCount", "area", "floorAreaRatio", "designChangeCount", "constructionCost"];
4
+ /**
5
+ * 프로젝트 정보관리 저장 → KPI 기준정보 반영.
6
+ * - 4개 양방향 필드: BuildingComplex 값 → KpiMetricValue
7
+ * - constructionPeriod: startDate/endDate → ceil(기간/30) 개월 (일방향)
8
+ * 대상 metric 이 kpi-admin 에서 비활성/삭제/개명됐으면 매칭 실패 → 조용히 skip.
9
+ */
10
+ export declare function syncProjectInfoToKpi(project: any, projectCode?: string): Promise<void>;
11
+ /**
12
+ * KPI 정보입력 저장 → 프로젝트 정보(BuildingComplex) 반영. (양방향 4필드만)
13
+ * constructionPeriod 는 일방향이므로 write-back 대상에서 제외.
14
+ * kpiMetrics / kpiMetricValues 는 KPI 화면이 이미 로드한 것을 그대로 사용.
15
+ */
16
+ export declare function syncKpiToProjectInfo(project: any, kpiMetrics: any[], kpiMetricValues: any[], projectCode?: string): Promise<void>;
@@ -0,0 +1,129 @@
1
+ import moment from 'moment-timezone';
2
+ import { getKpiMetrics, getKpiMetricValues, updateProjectCompleteStep1, updateBuildingComplexFields } from './complete-api';
3
+ /**
4
+ * 프로젝트 정보관리 ↔ KPI 정보입력 상호 동기.
5
+ *
6
+ * - 양방향(BIDIRECTIONAL_BC_KEYS): BuildingComplex 실컬럼을 가진 5필드(투입인력/연면적/
7
+ * 용적률/설계변경건/계획공사비)는 어느 화면에서 저장하든 반대편도 갱신 (last-write-wins).
8
+ * 두 저장 위치(BuildingComplex 컬럼 / KpiMetricValue row)는 그대로 두고 값만 맞춘다.
9
+ * - 일방향(constructionPeriod): 공사일정(startDate/endDate)은 KPI "계획기간"에 해당하므로
10
+ * 프로젝트 → KPI 단방향으로만 반영한다. (KPI 에서 일정을 역수정하지 않음)
11
+ *
12
+ * metric ↔ project 필드 매칭은 metric.name(라벨) 기준 — KPI_METRIC_KEY_MAPPING 과 동일.
13
+ */
14
+ /** metric 라벨 ↔ 프로젝트/BuildingComplex 필드 키. 라벨은 실제 DB KpiMetric.name 과 일치해야 함.
15
+ * (DB 확인: 계획공사비/계획공사기간/총 투입인력/용적률/총 설계 변경 건/연면적 존재) */
16
+ const LABEL_BY_KEY = {
17
+ constructionPeriod: '계획공사기간',
18
+ constructionCost: '계획공사비',
19
+ workerCount: '총 투입인력',
20
+ floorAreaRatio: '용적률',
21
+ designChangeCount: '총 설계 변경 건',
22
+ area: '연면적'
23
+ };
24
+ /** BuildingComplex 실컬럼 보유 → 프로젝트정보 ↔ KPI 양방향 동기 대상.
25
+ * (constructionCost 는 억원 소수 가능 → 정수화 대상 아님) */
26
+ export const BIDIRECTIONAL_BC_KEYS = ['workerCount', 'area', 'floorAreaRatio', 'designChangeCount', 'constructionCost'];
27
+ /** BuildingComplexPatch 에서 Int 로 정의된 컬럼 — write 시 정수화 필요. */
28
+ const BC_INT_KEYS = new Set(['workerCount', 'designChangeCount']);
29
+ function metricByProjectKey(metrics, projectKey) {
30
+ const label = LABEL_BY_KEY[projectKey];
31
+ if (!label)
32
+ return undefined;
33
+ return metrics.find((m) => m.name === label);
34
+ }
35
+ /**
36
+ * metric 의 기존 KpiMetricValue row 를 찾는다. (pc-tab1-plan 렌더/upsert 규칙과 동일)
37
+ * - MONTH : 전월(YYYY-MM) prefix row
38
+ * - 그 외 : (metricId, periodType) 단일 row
39
+ */
40
+ function findExistingRow(values, metric) {
41
+ if (metric.periodType === 'MONTH') {
42
+ const lastMonthYm = moment().tz('Asia/Seoul').subtract(1, 'month').format('YYYY-MM');
43
+ return values.find((v) => v.metricId === metric.id && v.periodType === 'MONTH' && (v.valueDate || '').startsWith(lastMonthYm));
44
+ }
45
+ return values.find((v) => v.metricId === metric.id && v.periodType === metric.periodType);
46
+ }
47
+ /**
48
+ * metric + value → KpiMetricValuePatch. 기존 row 가 있으면 그 id/valueDate 를 재사용해
49
+ * 서버의 (domain, metric, valueDate, org) upsert 가 동일 row 를 update 하도록 한다.
50
+ * org 는 projectId — 서버가 프로젝트 테넌트 도메인으로 스위치하는 데 필요.
51
+ */
52
+ function buildPatch(metric, value, existing, projectId) {
53
+ var _a, _b, _c, _d;
54
+ const today = moment().tz('Asia/Seoul');
55
+ const valueDate = (_a = existing === null || existing === void 0 ? void 0 : existing.valueDate) !== null && _a !== void 0 ? _a : (metric.periodType === 'MONTH' ? today.clone().subtract(1, 'month').format('YYYY-MM-01') : today.format('YYYY-MM-DD'));
56
+ return {
57
+ id: (_b = existing === null || existing === void 0 ? void 0 : existing.id) !== null && _b !== void 0 ? _b : crypto.randomUUID(),
58
+ metricId: metric.id,
59
+ value,
60
+ valueDate,
61
+ periodType: metric.periodType,
62
+ unit: (_d = (_c = existing === null || existing === void 0 ? void 0 : existing.unit) !== null && _c !== void 0 ? _c : metric.unit) !== null && _d !== void 0 ? _d : '',
63
+ org: projectId
64
+ };
65
+ }
66
+ /**
67
+ * 프로젝트 정보관리 저장 → KPI 기준정보 반영.
68
+ * - 4개 양방향 필드: BuildingComplex 값 → KpiMetricValue
69
+ * - constructionPeriod: startDate/endDate → ceil(기간/30) 개월 (일방향)
70
+ * 대상 metric 이 kpi-admin 에서 비활성/삭제/개명됐으면 매칭 실패 → 조용히 skip.
71
+ */
72
+ export async function syncProjectInfoToKpi(project, projectCode) {
73
+ if (!(project === null || project === void 0 ? void 0 : project.id))
74
+ return;
75
+ const [metrics, existing] = await Promise.all([getKpiMetrics(projectCode), getKpiMetricValues(project.id, projectCode)]);
76
+ const bc = project.buildingComplex || {};
77
+ const patches = [];
78
+ for (const key of BIDIRECTIONAL_BC_KEYS) {
79
+ const raw = bc[key];
80
+ if (raw === null || raw === undefined || raw === '')
81
+ continue;
82
+ const metric = metricByProjectKey(metrics, key);
83
+ if (!metric)
84
+ continue;
85
+ patches.push(buildPatch(metric, Number(raw), findExistingRow(existing, metric), project.id));
86
+ }
87
+ // 계획공사기간 — 프로젝트 착공~준공 일정 → KPI 단방향.
88
+ // calcDateDiff 는 strict 'YYYY-MM-DD' 파싱이라 DB 가 ISO datetime 을 주면 0 이 되므로
89
+ // 여기서는 lenient moment 파싱으로 일수를 구해 개월(올림)로 환산한다.
90
+ if (project.startDate && project.endDate) {
91
+ const start = moment(project.startDate);
92
+ const end = moment(project.endDate);
93
+ const metric = metricByProjectKey(metrics, 'constructionPeriod');
94
+ if (metric && start.isValid() && end.isValid()) {
95
+ // 개월 = 일수/30, 소수점 한자리 반올림 (예: 373일 → 12.4개월)
96
+ const months = Math.max(0, Math.round((end.diff(start, 'days') / 30) * 10) / 10);
97
+ patches.push(buildPatch(metric, months, findExistingRow(existing, metric), project.id));
98
+ }
99
+ }
100
+ if (patches.length) {
101
+ await updateProjectCompleteStep1(patches, projectCode);
102
+ }
103
+ }
104
+ /**
105
+ * KPI 정보입력 저장 → 프로젝트 정보(BuildingComplex) 반영. (양방향 4필드만)
106
+ * constructionPeriod 는 일방향이므로 write-back 대상에서 제외.
107
+ * kpiMetrics / kpiMetricValues 는 KPI 화면이 이미 로드한 것을 그대로 사용.
108
+ */
109
+ export async function syncKpiToProjectInfo(project, kpiMetrics, kpiMetricValues, projectCode) {
110
+ var _a;
111
+ const bcId = (_a = project === null || project === void 0 ? void 0 : project.buildingComplex) === null || _a === void 0 ? void 0 : _a.id;
112
+ if (!bcId)
113
+ return;
114
+ const patch = {};
115
+ for (const key of BIDIRECTIONAL_BC_KEYS) {
116
+ const metric = metricByProjectKey(kpiMetrics, key);
117
+ if (!metric)
118
+ continue;
119
+ const row = findExistingRow(kpiMetricValues, metric);
120
+ if (!row || row.value === undefined || row.value === null)
121
+ continue;
122
+ const num = Number(row.value);
123
+ patch[key] = BC_INT_KEYS.has(key) ? Math.round(num) : num;
124
+ }
125
+ if (Object.keys(patch).length) {
126
+ await updateBuildingComplexFields(bcId, patch, projectCode);
127
+ }
128
+ }
129
+ //# sourceMappingURL=kpi-project-sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kpi-project-sync.js","sourceRoot":"","sources":["../../client/shared/kpi-project-sync.ts"],"names":[],"mappings":"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","sourcesContent":["import moment from 'moment-timezone'\n\nimport { getKpiMetrics, getKpiMetricValues, updateProjectCompleteStep1, updateBuildingComplexFields } from './complete-api'\n\n/**\n * 프로젝트 정보관리 ↔ KPI 정보입력 상호 동기.\n *\n * - 양방향(BIDIRECTIONAL_BC_KEYS): BuildingComplex 실컬럼을 가진 5필드(투입인력/연면적/\n * 용적률/설계변경건/계획공사비)는 어느 화면에서 저장하든 반대편도 갱신 (last-write-wins).\n * 두 저장 위치(BuildingComplex 컬럼 / KpiMetricValue row)는 그대로 두고 값만 맞춘다.\n * - 일방향(constructionPeriod): 공사일정(startDate/endDate)은 KPI \"계획기간\"에 해당하므로\n * 프로젝트 → KPI 단방향으로만 반영한다. (KPI 에서 일정을 역수정하지 않음)\n *\n * metric ↔ project 필드 매칭은 metric.name(라벨) 기준 — KPI_METRIC_KEY_MAPPING 과 동일.\n */\n\n/** metric 라벨 ↔ 프로젝트/BuildingComplex 필드 키. 라벨은 실제 DB KpiMetric.name 과 일치해야 함.\n * (DB 확인: 계획공사비/계획공사기간/총 투입인력/용적률/총 설계 변경 건/연면적 존재) */\nconst LABEL_BY_KEY: Record<string, string> = {\n constructionPeriod: '계획공사기간',\n constructionCost: '계획공사비',\n workerCount: '총 투입인력',\n floorAreaRatio: '용적률',\n designChangeCount: '총 설계 변경 건',\n area: '연면적'\n}\n\n/** BuildingComplex 실컬럼 보유 → 프로젝트정보 ↔ KPI 양방향 동기 대상.\n * (constructionCost 는 억원 소수 가능 → 정수화 대상 아님) */\nexport const BIDIRECTIONAL_BC_KEYS = ['workerCount', 'area', 'floorAreaRatio', 'designChangeCount', 'constructionCost'] as const\n\n/** BuildingComplexPatch 에서 Int 로 정의된 컬럼 — write 시 정수화 필요. */\nconst BC_INT_KEYS = new Set(['workerCount', 'designChangeCount'])\n\nfunction metricByProjectKey(metrics: any[], projectKey: string): any | undefined {\n const label = LABEL_BY_KEY[projectKey]\n if (!label) return undefined\n return metrics.find((m: any) => m.name === label)\n}\n\n/**\n * metric 의 기존 KpiMetricValue row 를 찾는다. (pc-tab1-plan 렌더/upsert 규칙과 동일)\n * - MONTH : 전월(YYYY-MM) prefix row\n * - 그 외 : (metricId, periodType) 단일 row\n */\nfunction findExistingRow(values: any[], metric: any): any | undefined {\n if (metric.periodType === 'MONTH') {\n const lastMonthYm = moment().tz('Asia/Seoul').subtract(1, 'month').format('YYYY-MM')\n return values.find(\n (v: any) => v.metricId === metric.id && v.periodType === 'MONTH' && (v.valueDate || '').startsWith(lastMonthYm)\n )\n }\n return values.find((v: any) => v.metricId === metric.id && v.periodType === metric.periodType)\n}\n\n/**\n * metric + value → KpiMetricValuePatch. 기존 row 가 있으면 그 id/valueDate 를 재사용해\n * 서버의 (domain, metric, valueDate, org) upsert 가 동일 row 를 update 하도록 한다.\n * org 는 projectId — 서버가 프로젝트 테넌트 도메인으로 스위치하는 데 필요.\n */\nfunction buildPatch(metric: any, value: number, existing: any, projectId: string): any {\n const today = moment().tz('Asia/Seoul')\n const valueDate =\n existing?.valueDate ??\n (metric.periodType === 'MONTH' ? today.clone().subtract(1, 'month').format('YYYY-MM-01') : today.format('YYYY-MM-DD'))\n\n return {\n id: existing?.id ?? crypto.randomUUID(),\n metricId: metric.id,\n value,\n valueDate,\n periodType: metric.periodType,\n unit: existing?.unit ?? metric.unit ?? '',\n org: projectId\n }\n}\n\n/**\n * 프로젝트 정보관리 저장 → KPI 기준정보 반영.\n * - 4개 양방향 필드: BuildingComplex 값 → KpiMetricValue\n * - constructionPeriod: startDate/endDate → ceil(기간/30) 개월 (일방향)\n * 대상 metric 이 kpi-admin 에서 비활성/삭제/개명됐으면 매칭 실패 → 조용히 skip.\n */\nexport async function syncProjectInfoToKpi(project: any, projectCode?: string): Promise<void> {\n if (!project?.id) return\n\n const [metrics, existing] = await Promise.all([getKpiMetrics(projectCode), getKpiMetricValues(project.id, projectCode)])\n\n const bc = project.buildingComplex || {}\n const patches: any[] = []\n\n for (const key of BIDIRECTIONAL_BC_KEYS) {\n const raw = bc[key]\n if (raw === null || raw === undefined || raw === '') continue\n const metric = metricByProjectKey(metrics, key)\n if (!metric) continue\n patches.push(buildPatch(metric, Number(raw), findExistingRow(existing, metric), project.id))\n }\n\n // 계획공사기간 — 프로젝트 착공~준공 일정 → KPI 단방향.\n // calcDateDiff 는 strict 'YYYY-MM-DD' 파싱이라 DB 가 ISO datetime 을 주면 0 이 되므로\n // 여기서는 lenient moment 파싱으로 일수를 구해 개월(올림)로 환산한다.\n if (project.startDate && project.endDate) {\n const start = moment(project.startDate)\n const end = moment(project.endDate)\n const metric = metricByProjectKey(metrics, 'constructionPeriod')\n if (metric && start.isValid() && end.isValid()) {\n // 개월 = 일수/30, 소수점 한자리 반올림 (예: 373일 → 12.4개월)\n const months = Math.max(0, Math.round((end.diff(start, 'days') / 30) * 10) / 10)\n patches.push(buildPatch(metric, months, findExistingRow(existing, metric), project.id))\n }\n }\n\n if (patches.length) {\n await updateProjectCompleteStep1(patches, projectCode)\n }\n}\n\n/**\n * KPI 정보입력 저장 → 프로젝트 정보(BuildingComplex) 반영. (양방향 4필드만)\n * constructionPeriod 는 일방향이므로 write-back 대상에서 제외.\n * kpiMetrics / kpiMetricValues 는 KPI 화면이 이미 로드한 것을 그대로 사용.\n */\nexport async function syncKpiToProjectInfo(\n project: any,\n kpiMetrics: any[],\n kpiMetricValues: any[],\n projectCode?: string\n): Promise<void> {\n const bcId = project?.buildingComplex?.id\n if (!bcId) return\n\n const patch: Record<string, number> = {}\n for (const key of BIDIRECTIONAL_BC_KEYS) {\n const metric = metricByProjectKey(kpiMetrics, key)\n if (!metric) continue\n const row = findExistingRow(kpiMetricValues, metric)\n if (!row || row.value === undefined || row.value === null) continue\n const num = Number(row.value)\n patch[key] = BC_INT_KEYS.has(key) ? Math.round(num) : num\n }\n\n if (Object.keys(patch).length) {\n await updateBuildingComplexFields(bcId, patch, projectCode)\n }\n}\n"]}
@@ -0,0 +1,51 @@
1
+ .dark {
2
+ --md-sys-color-primary: rgb(173 198 255);
3
+ --md-sys-color-surface-tint: rgb(173 198 255);
4
+ --md-sys-color-on-primary: rgb(16 47 96);
5
+ --md-sys-color-primary-container: rgb(43 70 120);
6
+ --md-sys-color-on-primary-container: rgb(216 226 255);
7
+ --md-sys-color-secondary: rgb(167 200 255);
8
+ --md-sys-color-on-secondary: rgb(4 48 95);
9
+ --md-sys-color-secondary-container: rgb(36 71 119);
10
+ --md-sys-color-on-secondary-container: rgb(213 227 255);
11
+ --md-sys-color-tertiary: rgb(234 181 238);
12
+ --md-sys-color-on-tertiary: rgb(72 32 79);
13
+ --md-sys-color-tertiary-container: rgb(96 55 103);
14
+ --md-sys-color-on-tertiary-container: rgb(254 214 255);
15
+ --md-sys-color-error: rgb(255 180 171);
16
+ --md-sys-color-on-error: rgb(105 0 5);
17
+ --md-sys-color-error-container: rgb(147 0 10);
18
+ --md-sys-color-on-error-container: rgb(255 218 214);
19
+ --md-sys-color-background: rgb(17 19 24);
20
+ --md-sys-color-on-background: rgb(226 226 233);
21
+ --md-sys-color-surface: rgb(17 19 24);
22
+ --md-sys-color-on-surface: rgb(226 226 233);
23
+ --md-sys-color-surface-variant: rgb(68 71 79);
24
+ --md-sys-color-on-surface-variant: rgb(196 198 208);
25
+ --md-sys-color-outline: rgb(142 144 153);
26
+ --md-sys-color-outline-variant: rgb(68 71 79);
27
+ --md-sys-color-shadow: rgb(0 0 0);
28
+ --md-sys-color-scrim: rgb(0 0 0);
29
+ --md-sys-color-inverse-surface: rgb(226 226 233);
30
+ --md-sys-color-inverse-on-surface: rgb(47 48 54);
31
+ --md-sys-color-inverse-primary: rgb(68 94 145);
32
+ --md-sys-color-primary-fixed: rgb(216 226 255);
33
+ --md-sys-color-on-primary-fixed: rgb(0 26 65);
34
+ --md-sys-color-primary-fixed-dim: rgb(173 198 255);
35
+ --md-sys-color-on-primary-fixed-variant: rgb(43 70 120);
36
+ --md-sys-color-secondary-fixed: rgb(213 227 255);
37
+ --md-sys-color-on-secondary-fixed: rgb(0 27 60);
38
+ --md-sys-color-secondary-fixed-dim: rgb(167 200 255);
39
+ --md-sys-color-on-secondary-fixed-variant: rgb(36 71 119);
40
+ --md-sys-color-tertiary-fixed: rgb(254 214 255);
41
+ --md-sys-color-on-tertiary-fixed: rgb(48 9 57);
42
+ --md-sys-color-tertiary-fixed-dim: rgb(234 181 238);
43
+ --md-sys-color-on-tertiary-fixed-variant: rgb(96 55 103);
44
+ --md-sys-color-surface-dim: rgb(17 19 24);
45
+ --md-sys-color-surface-bright: rgb(55 57 62);
46
+ --md-sys-color-surface-container-lowest: rgb(12 14 19);
47
+ --md-sys-color-surface-container-low: rgb(26 27 32);
48
+ --md-sys-color-surface-container: rgb(30 31 37);
49
+ --md-sys-color-surface-container-high: rgb(40 42 47);
50
+ --md-sys-color-surface-container-highest: rgb(51 53 58);
51
+ }
@@ -0,0 +1,51 @@
1
+ .light {
2
+ --md-sys-color-primary: rgb(11 77 162);
3
+ --md-sys-color-surface-tint: rgb(68 94 145);
4
+ --md-sys-color-on-primary: rgb(255 255 255);
5
+ --md-sys-color-primary-container: rgb(216 226 255);
6
+ --md-sys-color-on-primary-container: rgb(43 70 120);
7
+ --md-sys-color-secondary: rgb(36, 41, 60);
8
+ --md-sys-color-on-secondary: rgb(255 255 255);
9
+ --md-sys-color-secondary-container: rgb(213 227 255);
10
+ --md-sys-color-on-secondary-container: rgb(36 71 119);
11
+ --md-sys-color-tertiary: rgb(122 79 128);
12
+ --md-sys-color-on-tertiary: rgb(255 255 255);
13
+ --md-sys-color-tertiary-container: rgb(254 214 255);
14
+ --md-sys-color-on-tertiary-container: rgb(96 55 103);
15
+ --md-sys-color-error: rgb(186 26 26);
16
+ --md-sys-color-on-error: rgb(255 255 255);
17
+ --md-sys-color-error-container: rgb(255 218 214);
18
+ --md-sys-color-on-error-container: rgb(147 0 10);
19
+ --md-sys-color-background: rgb(249 249 255);
20
+ --md-sys-color-on-background: rgb(26 27 32);
21
+ --md-sys-color-surface: rgb(249 249 255);
22
+ --md-sys-color-on-surface: rgb(26 27 32);
23
+ --md-sys-color-surface-variant: rgb(225 226 236);
24
+ --md-sys-color-on-surface-variant: rgb(68 71 79);
25
+ --md-sys-color-outline: rgb(117 119 127);
26
+ --md-sys-color-outline-variant: rgb(196 198 208);
27
+ --md-sys-color-shadow: rgb(0 0 0);
28
+ --md-sys-color-scrim: rgb(0 0 0);
29
+ --md-sys-color-inverse-surface: rgb(47 48 54);
30
+ --md-sys-color-inverse-on-surface: rgb(240 240 247);
31
+ --md-sys-color-inverse-primary: rgb(173 198 255);
32
+ --md-sys-color-primary-fixed: rgb(216 226 255);
33
+ --md-sys-color-on-primary-fixed: rgb(0 26 65);
34
+ --md-sys-color-primary-fixed-dim: rgb(173 198 255);
35
+ --md-sys-color-on-primary-fixed-variant: rgb(43 70 120);
36
+ --md-sys-color-secondary-fixed: rgb(213 227 255);
37
+ --md-sys-color-on-secondary-fixed: rgb(0 27 60);
38
+ --md-sys-color-secondary-fixed-dim: rgb(167 200 255);
39
+ --md-sys-color-on-secondary-fixed-variant: rgb(36 71 119);
40
+ --md-sys-color-tertiary-fixed: rgb(254 214 255);
41
+ --md-sys-color-on-tertiary-fixed: rgb(48 9 57);
42
+ --md-sys-color-tertiary-fixed-dim: rgb(234 181 238);
43
+ --md-sys-color-on-tertiary-fixed-variant: rgb(96 55 103);
44
+ --md-sys-color-surface-dim: rgb(217 217 224);
45
+ --md-sys-color-surface-bright: rgb(249 249 255);
46
+ --md-sys-color-surface-container-lowest: rgb(255 255 255);
47
+ --md-sys-color-surface-container-low: rgb(243 243 250);
48
+ --md-sys-color-surface-container: rgb(237 237 244);
49
+ --md-sys-color-surface-container-high: rgb(232 231 238);
50
+ --md-sys-color-surface-container-highest: rgb(226 226 233);
51
+ }