@itwin/reports-config-widget-react 0.1.0 → 0.2.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 (250) hide show
  1. package/.rush/temp/package-deps_rebuild.json +38 -34
  2. package/.rush/temp/shrinkwrap-deps.json +14 -6
  3. package/CHANGELOG.json +30 -0
  4. package/CHANGELOG.md +17 -1
  5. package/coverage/clover.xml +605 -610
  6. package/coverage/coverage-final.json +28 -27
  7. package/coverage/lcov-report/index.html +31 -31
  8. package/coverage/lcov-report/src/ReportsConfigWidget.ts.html +10 -10
  9. package/coverage/lcov-report/src/index.html +1 -1
  10. package/coverage/lcov-report/src/test/index.html +1 -1
  11. package/coverage/lcov-report/src/test/test-utils.tsx.html +22 -22
  12. package/coverage/lcov-report/src/widget/ReportsConfigUiProvider.tsx.html +7 -7
  13. package/coverage/lcov-report/src/widget/components/ActionPanel.tsx.html +29 -41
  14. package/coverage/lcov-report/src/widget/components/AddMappingsModal.tsx.html +31 -37
  15. package/coverage/lcov-report/src/widget/components/BulkExtractor.ts.html +270 -162
  16. package/coverage/lcov-report/src/widget/components/Constants.ts.html +4 -4
  17. package/coverage/lcov-report/src/widget/components/DeleteModal.tsx.html +21 -21
  18. package/coverage/lcov-report/src/widget/components/ExtractionStates/FailedExtractionState.tsx.html +6 -6
  19. package/coverage/lcov-report/src/widget/components/ExtractionStates/QueuedExtractionState.tsx.html +14 -14
  20. package/coverage/lcov-report/src/widget/components/ExtractionStates/RunningExtractionState.tsx.html +15 -15
  21. package/coverage/lcov-report/src/widget/components/ExtractionStates/StartingExtractionState.tsx.html +14 -14
  22. package/coverage/lcov-report/src/widget/components/ExtractionStates/SucceededExtractionState.tsx.html +22 -19
  23. package/coverage/lcov-report/src/widget/components/ExtractionStates/index.html +43 -43
  24. package/coverage/lcov-report/src/widget/components/ExtractionStatus.tsx.html +57 -57
  25. package/coverage/lcov-report/src/widget/components/ExtractionToast.tsx.html +208 -0
  26. package/coverage/lcov-report/src/widget/components/HorizontalTile.tsx.html +8 -8
  27. package/coverage/lcov-report/src/widget/components/LocalizedTablePaginator.tsx.html +8 -8
  28. package/coverage/lcov-report/src/widget/components/ReportAction.tsx.html +14 -20
  29. package/coverage/lcov-report/src/widget/components/ReportHorizontalTile.tsx.html +30 -33
  30. package/coverage/lcov-report/src/widget/components/ReportMappingHorizontalTile.tsx.html +469 -0
  31. package/coverage/lcov-report/src/widget/components/ReportMappings.tsx.html +183 -231
  32. package/coverage/lcov-report/src/widget/components/Reports.tsx.html +73 -43
  33. package/coverage/lcov-report/src/widget/components/ReportsContainer.tsx.html +10 -10
  34. package/coverage/lcov-report/src/widget/components/SearchBar.tsx.html +10 -10
  35. package/coverage/lcov-report/src/widget/components/SelectIModel.tsx.html +31 -31
  36. package/coverage/lcov-report/src/widget/components/index.html +100 -85
  37. package/coverage/lcov-report/src/widget/components/utils.tsx.html +32 -32
  38. package/coverage/lcov-report/src/widget/context/ReportsApiConfigContext.tsx.html +8 -8
  39. package/coverage/lcov-report/src/widget/context/index.html +1 -1
  40. package/coverage/lcov-report/src/widget/hooks/index.html +1 -1
  41. package/coverage/lcov-report/src/widget/hooks/useValidator.ts.html +7 -7
  42. package/coverage/lcov-report/src/widget/index.html +1 -1
  43. package/coverage/lcov.info +990 -1003
  44. package/jest.config.js +6 -0
  45. package/lib/cjs/test/AddMappingModal.test.d.ts +2 -0
  46. package/lib/cjs/test/AddMappingModal.test.d.ts.map +1 -0
  47. package/lib/cjs/test/AddMappingModal.test.js +277 -0
  48. package/lib/cjs/test/AddMappingModal.test.js.map +1 -0
  49. package/lib/cjs/test/BulkExtractor.test.d.ts +2 -0
  50. package/lib/cjs/test/BulkExtractor.test.d.ts.map +1 -0
  51. package/lib/cjs/test/BulkExtractor.test.js +182 -0
  52. package/lib/cjs/test/BulkExtractor.test.js.map +1 -0
  53. package/lib/cjs/test/DeleteModal.test.d.ts +2 -0
  54. package/lib/cjs/test/DeleteModal.test.d.ts.map +1 -0
  55. package/lib/cjs/test/DeleteModal.test.js +93 -0
  56. package/lib/cjs/test/DeleteModal.test.js.map +1 -0
  57. package/lib/cjs/test/ReportAction.test.js +2 -3
  58. package/lib/cjs/test/ReportAction.test.js.map +1 -1
  59. package/lib/cjs/test/ReportMappingHorizontalTile.test.d.ts +2 -0
  60. package/lib/cjs/test/ReportMappingHorizontalTile.test.d.ts.map +1 -0
  61. package/lib/cjs/test/ReportMappingHorizontalTile.test.js +340 -0
  62. package/lib/cjs/test/ReportMappingHorizontalTile.test.js.map +1 -0
  63. package/lib/cjs/test/ReportMappings.test.js +115 -267
  64. package/lib/cjs/test/ReportMappings.test.js.map +1 -1
  65. package/lib/cjs/test/Reports.test.js +1 -1
  66. package/lib/cjs/test/Reports.test.js.map +1 -1
  67. package/lib/cjs/tsconfig.tsbuildinfo +1 -1
  68. package/lib/cjs/widget/components/ActionPanel.d.ts.map +1 -1
  69. package/lib/cjs/widget/components/ActionPanel.js +4 -5
  70. package/lib/cjs/widget/components/ActionPanel.js.map +1 -1
  71. package/lib/cjs/widget/components/AddMappingsModal.d.ts +2 -2
  72. package/lib/cjs/widget/components/AddMappingsModal.d.ts.map +1 -1
  73. package/lib/cjs/widget/components/AddMappingsModal.js +5 -4
  74. package/lib/cjs/widget/components/AddMappingsModal.js.map +1 -1
  75. package/lib/cjs/widget/components/AddMappingsModal.scss +4 -3
  76. package/lib/cjs/widget/components/BulkExtractor.d.ts +18 -9
  77. package/lib/cjs/widget/components/BulkExtractor.d.ts.map +1 -1
  78. package/lib/cjs/widget/components/BulkExtractor.js +86 -57
  79. package/lib/cjs/widget/components/BulkExtractor.js.map +1 -1
  80. package/lib/cjs/widget/components/DeleteModal.js +1 -1
  81. package/lib/cjs/widget/components/DeleteModal.js.map +1 -1
  82. package/lib/cjs/widget/components/DeleteModal.scss +4 -3
  83. package/lib/cjs/widget/components/ExtractionStates/RunningExtractionState.js +1 -1
  84. package/lib/cjs/widget/components/ExtractionStates/RunningExtractionState.js.map +1 -1
  85. package/lib/cjs/widget/components/ExtractionStates/SucceededExtractionState.d.ts.map +1 -1
  86. package/lib/cjs/widget/components/ExtractionStates/SucceededExtractionState.js +1 -1
  87. package/lib/cjs/widget/components/ExtractionStates/SucceededExtractionState.js.map +1 -1
  88. package/lib/cjs/widget/components/ExtractionStatus.scss +15 -1
  89. package/lib/cjs/widget/components/ExtractionToast.d.ts +11 -0
  90. package/lib/cjs/widget/components/ExtractionToast.d.ts.map +1 -0
  91. package/lib/cjs/widget/components/ExtractionToast.js +33 -0
  92. package/lib/cjs/widget/components/ExtractionToast.js.map +1 -0
  93. package/lib/cjs/widget/components/HorizontalTile.scss +1 -1
  94. package/lib/cjs/widget/components/ReportAction.d.ts.map +1 -1
  95. package/lib/cjs/widget/components/ReportAction.js +3 -3
  96. package/lib/cjs/widget/components/ReportAction.js.map +1 -1
  97. package/lib/cjs/widget/components/ReportAction.scss +2 -2
  98. package/lib/cjs/widget/components/ReportHorizontalTile.d.ts.map +1 -1
  99. package/lib/cjs/widget/components/ReportHorizontalTile.js +1 -2
  100. package/lib/cjs/widget/components/ReportHorizontalTile.js.map +1 -1
  101. package/lib/cjs/widget/components/ReportMappingHorizontalTile.d.ts +13 -0
  102. package/lib/cjs/widget/components/ReportMappingHorizontalTile.d.ts.map +1 -0
  103. package/lib/cjs/widget/components/ReportMappingHorizontalTile.js +82 -0
  104. package/lib/cjs/widget/components/ReportMappingHorizontalTile.js.map +1 -0
  105. package/lib/cjs/widget/components/ReportMappings.d.ts +3 -1
  106. package/lib/cjs/widget/components/ReportMappings.d.ts.map +1 -1
  107. package/lib/cjs/widget/components/ReportMappings.js +39 -40
  108. package/lib/cjs/widget/components/ReportMappings.js.map +1 -1
  109. package/lib/cjs/widget/components/ReportMappings.scss +7 -7
  110. package/lib/cjs/widget/components/Reports.d.ts.map +1 -1
  111. package/lib/cjs/widget/components/Reports.js +13 -6
  112. package/lib/cjs/widget/components/Reports.js.map +1 -1
  113. package/lib/cjs/widget/components/Reports.scss +1 -1
  114. package/lib/cjs/widget/components/ReportsContainer.js +1 -1
  115. package/lib/cjs/widget/components/ReportsContainer.js.map +1 -1
  116. package/lib/cjs/widget/components/ReportsContainer.scss +2 -2
  117. package/lib/cjs/widget/components/SelectIModel.js +2 -2
  118. package/lib/cjs/widget/components/SelectIModel.js.map +1 -1
  119. package/lib/cjs/widget/components/SelectIModel.scss +4 -3
  120. package/lib/cjs/widget/components/utils.js +1 -1
  121. package/lib/cjs/widget/components/utils.js.map +1 -1
  122. package/lib/cjs/widget/components/utils.scss +2 -2
  123. package/lib/esm/test/AddMappingModal.test.d.ts +2 -0
  124. package/lib/esm/test/AddMappingModal.test.d.ts.map +1 -0
  125. package/lib/esm/test/AddMappingModal.test.js +253 -0
  126. package/lib/esm/test/AddMappingModal.test.js.map +1 -0
  127. package/lib/esm/test/BulkExtractor.test.d.ts +2 -0
  128. package/lib/esm/test/BulkExtractor.test.d.ts.map +1 -0
  129. package/lib/esm/test/BulkExtractor.test.js +158 -0
  130. package/lib/esm/test/BulkExtractor.test.js.map +1 -0
  131. package/lib/esm/test/DeleteModal.test.d.ts +2 -0
  132. package/lib/esm/test/DeleteModal.test.d.ts.map +1 -0
  133. package/lib/esm/test/DeleteModal.test.js +69 -0
  134. package/lib/esm/test/DeleteModal.test.js.map +1 -0
  135. package/lib/esm/test/ReportAction.test.js +2 -3
  136. package/lib/esm/test/ReportAction.test.js.map +1 -1
  137. package/lib/esm/test/ReportMappingHorizontalTile.test.d.ts +2 -0
  138. package/lib/esm/test/ReportMappingHorizontalTile.test.d.ts.map +1 -0
  139. package/lib/esm/test/ReportMappingHorizontalTile.test.js +316 -0
  140. package/lib/esm/test/ReportMappingHorizontalTile.test.js.map +1 -0
  141. package/lib/esm/test/ReportMappings.test.js +117 -269
  142. package/lib/esm/test/ReportMappings.test.js.map +1 -1
  143. package/lib/esm/test/Reports.test.js +1 -1
  144. package/lib/esm/test/Reports.test.js.map +1 -1
  145. package/lib/esm/tsconfig.tsbuildinfo +1 -1
  146. package/lib/esm/widget/components/ActionPanel.d.ts.map +1 -1
  147. package/lib/esm/widget/components/ActionPanel.js +4 -5
  148. package/lib/esm/widget/components/ActionPanel.js.map +1 -1
  149. package/lib/esm/widget/components/AddMappingsModal.d.ts +2 -2
  150. package/lib/esm/widget/components/AddMappingsModal.d.ts.map +1 -1
  151. package/lib/esm/widget/components/AddMappingsModal.js +4 -5
  152. package/lib/esm/widget/components/AddMappingsModal.js.map +1 -1
  153. package/lib/esm/widget/components/AddMappingsModal.scss +4 -3
  154. package/lib/esm/widget/components/BulkExtractor.d.ts +18 -9
  155. package/lib/esm/widget/components/BulkExtractor.d.ts.map +1 -1
  156. package/lib/esm/widget/components/BulkExtractor.js +86 -57
  157. package/lib/esm/widget/components/BulkExtractor.js.map +1 -1
  158. package/lib/esm/widget/components/DeleteModal.js +1 -1
  159. package/lib/esm/widget/components/DeleteModal.js.map +1 -1
  160. package/lib/esm/widget/components/DeleteModal.scss +4 -3
  161. package/lib/esm/widget/components/ExtractionStates/RunningExtractionState.js +1 -1
  162. package/lib/esm/widget/components/ExtractionStates/RunningExtractionState.js.map +1 -1
  163. package/lib/esm/widget/components/ExtractionStates/SucceededExtractionState.d.ts.map +1 -1
  164. package/lib/esm/widget/components/ExtractionStates/SucceededExtractionState.js +1 -1
  165. package/lib/esm/widget/components/ExtractionStates/SucceededExtractionState.js.map +1 -1
  166. package/lib/esm/widget/components/ExtractionStatus.scss +15 -1
  167. package/lib/esm/widget/components/ExtractionToast.d.ts +11 -0
  168. package/lib/esm/widget/components/ExtractionToast.d.ts.map +1 -0
  169. package/lib/esm/widget/components/ExtractionToast.js +25 -0
  170. package/lib/esm/widget/components/ExtractionToast.js.map +1 -0
  171. package/lib/esm/widget/components/HorizontalTile.scss +1 -1
  172. package/lib/esm/widget/components/ReportAction.d.ts.map +1 -1
  173. package/lib/esm/widget/components/ReportAction.js +3 -3
  174. package/lib/esm/widget/components/ReportAction.js.map +1 -1
  175. package/lib/esm/widget/components/ReportAction.scss +2 -2
  176. package/lib/esm/widget/components/ReportHorizontalTile.d.ts.map +1 -1
  177. package/lib/esm/widget/components/ReportHorizontalTile.js +1 -2
  178. package/lib/esm/widget/components/ReportHorizontalTile.js.map +1 -1
  179. package/lib/esm/widget/components/ReportMappingHorizontalTile.d.ts +13 -0
  180. package/lib/esm/widget/components/ReportMappingHorizontalTile.d.ts.map +1 -0
  181. package/lib/esm/widget/components/ReportMappingHorizontalTile.js +75 -0
  182. package/lib/esm/widget/components/ReportMappingHorizontalTile.js.map +1 -0
  183. package/lib/esm/widget/components/ReportMappings.d.ts +3 -1
  184. package/lib/esm/widget/components/ReportMappings.d.ts.map +1 -1
  185. package/lib/esm/widget/components/ReportMappings.js +40 -41
  186. package/lib/esm/widget/components/ReportMappings.js.map +1 -1
  187. package/lib/esm/widget/components/ReportMappings.scss +7 -7
  188. package/lib/esm/widget/components/Reports.d.ts.map +1 -1
  189. package/lib/esm/widget/components/Reports.js +15 -8
  190. package/lib/esm/widget/components/Reports.js.map +1 -1
  191. package/lib/esm/widget/components/Reports.scss +1 -1
  192. package/lib/esm/widget/components/ReportsContainer.js +1 -1
  193. package/lib/esm/widget/components/ReportsContainer.js.map +1 -1
  194. package/lib/esm/widget/components/ReportsContainer.scss +2 -2
  195. package/lib/esm/widget/components/SelectIModel.js +2 -2
  196. package/lib/esm/widget/components/SelectIModel.js.map +1 -1
  197. package/lib/esm/widget/components/SelectIModel.scss +4 -3
  198. package/lib/esm/widget/components/utils.js +1 -1
  199. package/lib/esm/widget/components/utils.js.map +1 -1
  200. package/lib/esm/widget/components/utils.scss +2 -2
  201. package/lib/public/locales/en/ReportsConfigWidget.json +5 -0
  202. package/package.json +7 -4
  203. package/public/locales/en/ReportsConfigWidget.json +5 -0
  204. package/reports-config-widget-react.build.error.log +10 -6
  205. package/reports-config-widget-react.build.log +51 -46
  206. package/src/test/AddMappingModal.test.tsx +315 -0
  207. package/src/test/BulkExtractor.test.ts +301 -0
  208. package/src/test/DeleteModal.test.tsx +118 -0
  209. package/src/test/ReportAction.test.tsx +2 -4
  210. package/src/test/ReportMappingHorizontalTile.test.tsx +451 -0
  211. package/src/test/ReportMappings.test.tsx +154 -549
  212. package/src/test/Reports.test.tsx +1 -1
  213. package/src/widget/components/ActionPanel.tsx +19 -23
  214. package/src/widget/components/AddMappingsModal.scss +4 -3
  215. package/src/widget/components/AddMappingsModal.tsx +4 -6
  216. package/src/widget/components/BulkExtractor.ts +97 -61
  217. package/src/widget/components/DeleteModal.scss +4 -3
  218. package/src/widget/components/DeleteModal.tsx +1 -1
  219. package/src/widget/components/ExtractionStates/RunningExtractionState.tsx +1 -1
  220. package/src/widget/components/ExtractionStates/SucceededExtractionState.tsx +1 -0
  221. package/src/widget/components/ExtractionStatus.scss +15 -1
  222. package/src/widget/components/ExtractionToast.tsx +41 -0
  223. package/src/widget/components/HorizontalTile.scss +1 -1
  224. package/src/widget/components/ReportAction.scss +2 -2
  225. package/src/widget/components/ReportAction.tsx +1 -3
  226. package/src/widget/components/ReportHorizontalTile.tsx +1 -2
  227. package/src/widget/components/ReportMappingHorizontalTile.tsx +128 -0
  228. package/src/widget/components/ReportMappings.scss +7 -7
  229. package/src/widget/components/ReportMappings.tsx +89 -105
  230. package/src/widget/components/Reports.scss +1 -1
  231. package/src/widget/components/Reports.tsx +18 -8
  232. package/src/widget/components/ReportsContainer.scss +2 -2
  233. package/src/widget/components/ReportsContainer.tsx +1 -1
  234. package/src/widget/components/SelectIModel.scss +4 -3
  235. package/src/widget/components/SelectIModel.tsx +2 -2
  236. package/src/widget/components/utils.scss +2 -2
  237. package/src/widget/components/utils.tsx +1 -1
  238. package/coverage/lcov-report/src/widget/components/Extraction.tsx.html +0 -1030
  239. package/lib/cjs/widget/components/Extraction.d.ts +0 -28
  240. package/lib/cjs/widget/components/Extraction.d.ts.map +0 -1
  241. package/lib/cjs/widget/components/Extraction.js +0 -190
  242. package/lib/cjs/widget/components/Extraction.js.map +0 -1
  243. package/lib/cjs/widget/components/Extraction.scss +0 -39
  244. package/lib/esm/widget/components/Extraction.d.ts +0 -28
  245. package/lib/esm/widget/components/Extraction.d.ts.map +0 -1
  246. package/lib/esm/widget/components/Extraction.js +0 -166
  247. package/lib/esm/widget/components/Extraction.js.map +0 -1
  248. package/lib/esm/widget/components/Extraction.scss +0 -39
  249. package/src/widget/components/Extraction.scss +0 -39
  250. package/src/widget/components/Extraction.tsx +0 -315
@@ -291,7 +291,7 @@ describe("Reports View", () => {
291
291
 
292
292
  await waitForElementToBeRemoved(() => screen.getByText(/loading/i));
293
293
 
294
- const searchButton = within(screen.getByTestId(/search-bar/i)).getByRole(
294
+ const searchButton = within(screen.getByTestId(/rcw-search-bar/i)).getByRole(
295
295
  "button"
296
296
  );
297
297
  await user.click(searchButton);
@@ -26,29 +26,25 @@ const ActionPanel = ({
26
26
  isLoading = false,
27
27
  }: ActionPanelProps): JSX.Element => {
28
28
  return (
29
- <div id="action">
30
- <div className="rcw-action-panel">
31
- {isLoading && <LoadingSpinner />}
32
- <Button
33
- disabled={isSavingDisabled || isLoading}
34
- styleType="high-visibility"
35
- id="save-app"
36
- onClick={onAction}
37
- >
38
- {actionLabel}
39
- </Button>
40
- <Button
41
- styleType="default"
42
- type="button"
43
- id="cancel"
44
- onClick={onCancel}
45
- disabled={isCancelDisabled || isLoading}
46
- >
47
- {ReportsConfigWidget.localization.getLocalizedString(
48
- "ReportsConfigWidget:Cancel"
49
- )}
50
- </Button>
51
- </div>
29
+ <div className="rcw-action-panel">
30
+ {isLoading && <LoadingSpinner />}
31
+ <Button
32
+ disabled={isSavingDisabled || isLoading}
33
+ styleType="high-visibility"
34
+ onClick={onAction}
35
+ >
36
+ {actionLabel}
37
+ </Button>
38
+ <Button
39
+ styleType="default"
40
+ type="button"
41
+ onClick={onCancel}
42
+ disabled={isCancelDisabled || isLoading}
43
+ >
44
+ {ReportsConfigWidget.localization.getLocalizedString(
45
+ "ReportsConfigWidget:Cancel"
46
+ )}
47
+ </Button>
52
48
  </div>
53
49
  );
54
50
  };
@@ -4,15 +4,16 @@
4
4
  *--------------------------------------------------------------------------------------------*/
5
5
  @import "~@itwin/itwinui-css/scss/variables";
6
6
 
7
- .add-mappings-container {
7
+ .rcw-add-mappings-container {
8
8
  display: flex;
9
9
  flex-direction: column;
10
10
  gap: $iui-baseline;
11
11
  width: 45vw;
12
12
  min-width: 100%;
13
13
  overflow: auto;
14
- .add-mappings-table {
14
+
15
+ .rcw-add-mappings-table {
15
16
  flex-grow: 1;
16
17
  overflow: auto;
17
18
  }
18
- }
19
+ }
@@ -50,13 +50,13 @@ interface AddMappingsModalProps {
50
50
  returnFn: () => Promise<void>;
51
51
  }
52
52
 
53
- const AddMappingsModal = ({
53
+ export const AddMappingsModal = ({
54
54
  reportId,
55
55
  existingMappings,
56
56
  show,
57
57
  returnFn,
58
58
  }: AddMappingsModalProps) => {
59
- const [isLoading, setIsLoading] = useState<boolean>(true);
59
+ const [isLoading, setIsLoading] = useState<boolean>(false);
60
60
  const [selectedMappings, setSelectedMappings] = useState<Mapping[]>([]);
61
61
  const [selectedIModelId, setSelectediModelId] = useState<string>("");
62
62
  const [mappings, setMappings] = useState<Mapping[]>([]);
@@ -135,7 +135,7 @@ const AddMappingsModal = ({
135
135
  }}
136
136
  style={{ display: "flex", flexDirection: "column", maxHeight: "77vh" }}
137
137
  >
138
- <div className="add-mappings-container">
138
+ <div className="rcw-add-mappings-container">
139
139
  <SelectIModel
140
140
  selectedIModelId={selectedIModelId}
141
141
  setSelectedIModelId={setSelectediModelId}
@@ -143,7 +143,7 @@ const AddMappingsModal = ({
143
143
  <Table<MappingType>
144
144
  data={isLoading ? [] : mappings}
145
145
  columns={mappingsColumns}
146
- className="add-mappings-table"
146
+ className="rcw-add-mappings-table"
147
147
  emptyTableContent={ReportsConfigWidget.localization.getLocalizedString(
148
148
  "ReportsConfigWidget:NoMappingsAvailable"
149
149
  )}
@@ -171,5 +171,3 @@ const AddMappingsModal = ({
171
171
  </Modal>
172
172
  );
173
173
  };
174
-
175
- export default AddMappingsModal;
@@ -16,67 +16,80 @@ export type ReportMappingAndMapping = ReportMapping & {
16
16
  };
17
17
 
18
18
  export default class BulkExtractor {
19
- private _reportRunIds = new Map<string, string[]>();
20
19
  private _reportsClientApi: ReportsClient;
21
20
  private _extractionClientApi: ExtractionClient;
22
21
  private _accessToken: () => Promise<string>;
23
- private _reportStates = new Map<string, ExtractionStates>();
24
- private _timeFetched = new Date();
25
- private _reportIds: string[];
26
22
 
27
- constructor(apiConfig: ReportsApiConfig, reportIds: string[]) {
23
+ private _reportIModels = new Map<string, string[]>(); // key: reportId, value: iModels
24
+ private _iModelStates = new Map<string, ExtractorState>(); // key: iModelId, value: state
25
+ private _timeFetched = new Date();
26
+ private _iModelRun = new Map<string, string>(); // key: iModelId, value: runId
27
+ private _iModelToast = new Set<string>();
28
+ private _successfulExtractionToast: (iModelName: string, odataFeedUrl: string) => void;
29
+ private _failedExtractionToast: (iModelName: string) => void;
30
+ private _setJobRunning: React.Dispatch<React.SetStateAction<boolean>> | undefined;
31
+ private _iModels: string[] = [];
32
+
33
+ constructor(
34
+ apiConfig: ReportsApiConfig,
35
+ successfulExtractionToast: (iModelName: string, odataFeedUrl: string) => void,
36
+ failedExtractionToast: (iModelName: string) => void,
37
+ ) {
28
38
  const url = generateUrl(REPORTING_BASE_PATH, apiConfig.baseUrl);
29
39
  this._reportsClientApi = new ReportsClient(url);
30
40
  this._extractionClientApi = new ExtractionClient(url);
31
41
  this._accessToken = apiConfig.getAccessToken;
32
- this._reportIds = reportIds;
42
+ this._successfulExtractionToast = successfulExtractionToast;
43
+ this._failedExtractionToast = failedExtractionToast;
33
44
  }
34
45
 
35
- private async getStates(reportIds: string[]): Promise<Map<string, ExtractionStates>> {
36
- const stateByReportId = new Map<string, ExtractionStates>();
37
- const stateByRunId = new Map<string, ExtractorState>();
38
-
39
- for (const reportId of reportIds) {
40
- const runs = this._reportRunIds.get(reportId);
41
- if (!runs) {
42
- stateByReportId.set(reportId, ExtractionStates.None);
43
- continue;
44
- }
45
-
46
- const states: ExtractorState[] = [];
47
- for (const runId of runs) {
48
- const state = stateByRunId.get(runId);
49
- if (state) {
50
- states.push(state);
51
- } else {
52
- const runState = await this.getSingleState(runId, await this._accessToken());
53
- states.push(runState);
54
- stateByRunId.set(runId, runState);
55
- }
46
+ private async fetchStates(): Promise<void> {
47
+ for (const [iModelId, runId] of this._iModelRun) {
48
+ const state = await this.getState(runId);
49
+ if (state === ExtractorState.Succeeded || state === ExtractorState.Failed) {
50
+ this._iModelRun.delete(iModelId);
56
51
  }
57
- const finalState = BulkExtractor.getFinalState(states);
58
- stateByReportId.set(reportId, finalState);
52
+ this._iModelStates.set(iModelId, state);
59
53
  }
60
- return stateByReportId;
61
54
  }
62
55
 
63
- private async fetchStates(): Promise<void> {
64
- this._reportStates = await this.getStates(this._reportIds);
56
+ public async getReportState(reportId: string): Promise<ExtractionStates> {
57
+ if ((new Date().getTime() - this._timeFetched.getTime()) > STATUS_CHECK_INTERVAL) {
58
+ this._timeFetched = new Date();
59
+ await this.fetchStates();
60
+ }
61
+
62
+ const iModels = this._reportIModels.get(reportId);
63
+ if (!iModels) return ExtractionStates.None;
64
+ const states: ExtractorState[] = [];
65
+ for (const iModelId of iModels) {
66
+ const state = this._iModelStates.get(iModelId);
67
+ if (!state) continue;
68
+ states.push(state);
69
+ }
70
+ return BulkExtractor.getFinalState(states);
65
71
  }
66
72
 
67
- public getState(reportId: string): ExtractionStates {
73
+ public async getIModelState(iModelId: string, iModelName: string, odataFeedUrl: string): Promise<ExtractionStates> {
68
74
  if ((new Date().getTime() - this._timeFetched.getTime()) > STATUS_CHECK_INTERVAL) {
69
75
  this._timeFetched = new Date();
70
- this.fetchStates().catch((e) =>
71
- /* eslint-disable no-console */
72
- console.error(e)
73
- );
76
+ await this.fetchStates();
74
77
  }
75
- return this._reportStates.get(reportId) ?? ExtractionStates.None;
76
- }
77
78
 
78
- public clearJob(reportId: string): void {
79
- this._reportRunIds.delete(reportId);
79
+ const state = this._iModelStates.get(iModelId);
80
+ if (!state) return ExtractionStates.None;
81
+ if (!this._iModelToast.has(iModelId)) {
82
+ if (state === ExtractorState.Succeeded) {
83
+ this._successfulExtractionToast(iModelName, odataFeedUrl);
84
+ this._iModelToast.add(iModelId);
85
+ this.checkRunning();
86
+ } else if (state === ExtractorState.Failed) {
87
+ this._failedExtractionToast(iModelName);
88
+ this._iModelToast.add(iModelId);
89
+ this.checkRunning();
90
+ }
91
+ }
92
+ return BulkExtractor.getFinalState([state]);
80
93
  }
81
94
 
82
95
  private static getFinalState(states: ExtractorState[]): ExtractionStates {
@@ -95,8 +108,9 @@ export default class BulkExtractor {
95
108
  return ExtractionStates.Failed;
96
109
  }
97
110
 
98
- private async getSingleState(runId: string, accessToken: string): Promise<ExtractorState> {
111
+ private async getState(runId: string): Promise<ExtractorState> {
99
112
  try {
113
+ const accessToken = await this._accessToken();
100
114
  const response = await this._extractionClientApi.getExtractionStatus(accessToken, runId);
101
115
  return response.state;
102
116
  } catch (error: any) {
@@ -105,32 +119,32 @@ export default class BulkExtractor {
105
119
  return ExtractorState.Failed;
106
120
  }
107
121
 
108
- public async startJobs(reportIds: string[]): Promise<void> {
122
+ private checkRunning(): void {
123
+ if (this._setJobRunning) {
124
+ let allFinished = true;
125
+ this._iModels.forEach((iModelId) => {
126
+ const state = this._iModelStates.get(iModelId);
127
+ if (state === ExtractorState.Queued || state === ExtractorState.Running) {
128
+ allFinished = false;
129
+ }
130
+ });
131
+
132
+ this._setJobRunning(!allFinished);
133
+ }
134
+ }
135
+
136
+ public async runReportExtractions(reportIds: string[]): Promise<void> {
109
137
  const reportIModelIds = new Map<string, string[]>();
110
138
  for (const reportId of reportIds) {
111
139
  const reportIModels = await this.fetchReportIModels(reportId);
112
140
  reportIModelIds.set(reportId, reportIModels);
113
- this._reportStates.set(reportId, ExtractionStates.Starting);
141
+ this._reportIModels.set(reportId, reportIModels);
114
142
  }
115
143
  const iModels = new Set(Array.from(reportIModelIds.values()).flat());
116
- const extractionMapPromise =
117
- Array.from(iModels).map(async (iModel): Promise<[string, string | undefined]> => {
118
- const run = await this.runExtraction(iModel);
119
- return [iModel, run];
120
- });
121
144
 
122
- const extractionMap = await Promise.all(extractionMapPromise);
123
- const extractionByIModel = new Map<string, string | undefined>(extractionMap);
124
- reportIds.forEach((reportId) => {
125
- const reportIModels = reportIModelIds.get(reportId)!;
126
- const runs: string[] = [];
127
- reportIModels.forEach((iModelId) => {
128
- const runId = extractionByIModel.get(iModelId);
129
- if (runId)
130
- runs.push(runId);
131
- });
132
- this._reportRunIds.set(reportId, runs);
133
- });
145
+ for (const iModel of iModels) {
146
+ await this.runIModelExtractions([iModel]);
147
+ }
134
148
  }
135
149
 
136
150
  private async runExtraction(iModelId: string): Promise<string | undefined> {
@@ -139,6 +153,7 @@ export default class BulkExtractor {
139
153
  await this._accessToken(),
140
154
  iModelId
141
155
  );
156
+ this._iModelToast.delete(iModelId);
142
157
  return response.id;
143
158
  } catch (error: any) {
144
159
  handleError(error.status);
@@ -146,6 +161,27 @@ export default class BulkExtractor {
146
161
  return undefined;
147
162
  }
148
163
 
164
+ public async runIModelExtraction(iModelId: string): Promise<void> {
165
+ return this.runIModelExtractions([iModelId]);
166
+ }
167
+
168
+ public setHook(setJobRunning: React.Dispatch<React.SetStateAction<boolean>>, iModels: string[]): void {
169
+ this._setJobRunning = setJobRunning;
170
+ this._iModels = iModels;
171
+ this.checkRunning();
172
+ }
173
+
174
+ public async runIModelExtractions(iModels: string[]): Promise<void> {
175
+ for (const iModelId of iModels) {
176
+ const run = await this.runExtraction(iModelId);
177
+ if (run) {
178
+ this._iModelStates.set(iModelId, ExtractorState.Queued);
179
+ this._iModelRun.set(iModelId, run);
180
+ }
181
+ this.checkRunning();
182
+ }
183
+ }
184
+
149
185
  private async fetchReportIModels(reportId: string): Promise<string[]> {
150
186
  const reportMappings = await this._reportsClientApi.getReportMappings(
151
187
  await this._accessToken(),
@@ -4,15 +4,16 @@
4
4
  *--------------------------------------------------------------------------------------------*/
5
5
  @import "~@itwin/itwinui-css/scss/variables";
6
6
 
7
- .delete-modal-body-text {
7
+ .rcw-delete-modal-body-text {
8
8
  display: flex;
9
9
  gap: $iui-xs;
10
10
  flex-wrap: wrap;
11
- > strong {
11
+
12
+ >strong {
12
13
  min-width: 0px;
13
14
  }
14
15
  }
15
16
 
16
17
  .rcw-loading-delete {
17
18
  margin-right: $iui-s;
18
- }
19
+ }
@@ -57,7 +57,7 @@ export const DeleteModal = ({
57
57
  }}
58
58
  >
59
59
  <ModalContent>
60
- <div className="delete-modal-body-text">
60
+ <div className="rcw-delete-modal-body-text">
61
61
  <Leading>
62
62
  {ReportsConfigWidget.localization.getLocalizedString(
63
63
  "ReportsConfigWidget:AreYouSureYouWantToDelete"
@@ -11,7 +11,7 @@ export const RunningExtractionState = () => (
11
11
  title={ReportsConfigWidget.localization.getLocalizedString(
12
12
  "ReportsConfigWidget:Running"
13
13
  )}
14
- className="rcw-extraction-status"
14
+ className="rcw-extraction-status-running"
15
15
  >
16
16
  <ProgressRadial size="x-small" indeterminate />
17
17
  </div>
@@ -21,6 +21,7 @@ export const SucceededExtractionState = ({ animation, onAnimationEnd }: Extracti
21
21
  >
22
22
  <div
23
23
  className={`rcw-status-icon`}
24
+ data-testid="rcw-success-animation"
24
25
  style={{
25
26
  animationName: animation ? "rcw-fade-out" : "",
26
27
  animationDelay: ANIMATION_DELAY,
@@ -6,10 +6,24 @@
6
6
 
7
7
  .rcw-extraction-status {
8
8
  align-items: center;
9
- margin: auto $iui-s * 2;
10
9
  height: 38px;
11
10
  display: flex;
12
11
 
12
+ .rcw-status-icon {
13
+ width: $iui-icons-default;
14
+ height: $iui-icons-default;
15
+ align-items: center;
16
+ margin: auto $iui-xs * 3;
17
+ display: flex;
18
+ }
19
+ }
20
+
21
+ .rcw-extraction-status-running {
22
+ align-items: center;
23
+ height: 38px;
24
+ display: flex;
25
+ margin: auto $iui-xs * 3;
26
+
13
27
  .rcw-status-icon {
14
28
  width: $iui-icons-default;
15
29
  height: $iui-icons-default;
@@ -0,0 +1,41 @@
1
+ /*---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
3
+ * See LICENSE.md in the project root for license terms and full copyright notice.
4
+ *--------------------------------------------------------------------------------------------*/
5
+ import React from "react";
6
+ import { Text } from "@itwin/itwinui-react";
7
+ import { ReportsConfigWidget } from "../../ReportsConfigWidget";
8
+
9
+ interface SuccessfulExtractionToastProps extends ExtractionToastProps {
10
+ odataFeedUrl: string;
11
+ }
12
+
13
+ interface ExtractionToastProps {
14
+ iModelName: string;
15
+ }
16
+
17
+ export const SuccessfulExtractionToast = ({ iModelName, odataFeedUrl }: SuccessfulExtractionToastProps) => {
18
+ const onClick = async (e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
19
+ e.preventDefault();
20
+ await navigator.clipboard.writeText(odataFeedUrl);
21
+ };
22
+ return (
23
+ <div>
24
+ <Text>{ReportsConfigWidget.localization.getLocalizedString("ReportsConfigWidget:ExtractionSuccess")}{iModelName}</Text>
25
+ <a
26
+ href="#"
27
+ onClick={onClick}
28
+ >
29
+ {ReportsConfigWidget.localization.getLocalizedString("ReportsConfigWidget:CopyODataUrl")}
30
+ </a>
31
+ </div>
32
+ );
33
+ };
34
+
35
+ export const FailedExtractionToast = ({ iModelName }: ExtractionToastProps) => {
36
+ return (
37
+ <div>
38
+ <Text>{ReportsConfigWidget.localization.getLocalizedString("ReportsConfigWidget:ExtractionFailed")}{iModelName}</Text>
39
+ </div>
40
+ );
41
+ };
@@ -36,8 +36,8 @@
36
36
  margin-left: 0;
37
37
  align-self: center;
38
38
  min-width: 36px;
39
- margin-right: $iui-xs;
40
39
  flex-shrink: 0;
40
+ display: flex;
41
41
  }
42
42
  }
43
43
 
@@ -8,9 +8,9 @@
8
8
  overflow-x: auto;
9
9
  height: 100%;
10
10
 
11
- .details-form {
11
+ .rcw-details-form {
12
12
  display: flex;
13
13
  flex-direction: column;
14
14
  gap: $iui-baseline;
15
15
  }
16
- }
16
+ }
@@ -82,7 +82,7 @@ const ReportAction = ({ iTwinId, report, returnFn }: ReportActionProps) => {
82
82
  legend={ReportsConfigWidget.localization.getLocalizedString(
83
83
  "ReportsConfigWidget:ReportDetails"
84
84
  )}
85
- className="details-form"
85
+ className="rcw-details-form"
86
86
  >
87
87
  <Small className="field-legend">
88
88
  {ReportsConfigWidget.localization.getLocalizedString(
@@ -90,7 +90,6 @@ const ReportAction = ({ iTwinId, report, returnFn }: ReportActionProps) => {
90
90
  )}
91
91
  </Small>
92
92
  <LabeledInput
93
- id="name"
94
93
  name="name"
95
94
  label={ReportsConfigWidget.localization.getLocalizedString(
96
95
  "ReportsConfigWidget:Name"
@@ -117,7 +116,6 @@ const ReportAction = ({ iTwinId, report, returnFn }: ReportActionProps) => {
117
116
  }}
118
117
  />
119
118
  <LabeledInput
120
- id="description"
121
119
  name="description"
122
120
  label={ReportsConfigWidget.localization.getLocalizedString(
123
121
  "ReportsConfigWidget:Description"
@@ -56,7 +56,7 @@ export const ReportHorizontalTile = (props: ReportHorizontalTileProps) => {
56
56
  if (jobStarted) {
57
57
  window.clearInterval(interval.current);
58
58
  interval.current = window.setInterval(async () => {
59
- const state = props.bulkExtractor.getState(props.report.id);
59
+ const state = await props.bulkExtractor.getReportState(props.report.id);
60
60
  if (state) {
61
61
  setExtractionState(state);
62
62
  if (state === ExtractionStates.Failed || state === ExtractionStates.Succeeded) {
@@ -121,7 +121,6 @@ export const ReportHorizontalTile = (props: ReportHorizontalTileProps) => {
121
121
  <ExtractionStatus
122
122
  state={extractionState}
123
123
  clearExtractionState={() => {
124
- props.bulkExtractor.clearJob(props.report.id);
125
124
  setExtractionState(ExtractionStates.None);
126
125
  }}
127
126
  ></ExtractionStatus>
@@ -0,0 +1,128 @@
1
+ /*---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
3
+ * See LICENSE.md in the project root for license terms and full copyright notice.
4
+ *--------------------------------------------------------------------------------------------*/
5
+ import { useCallback, useEffect, useRef, useState } from "react";
6
+ import React from "react";
7
+ import type BulkExtractor from "./BulkExtractor";
8
+ import { ExtractionStates, ExtractionStatus } from "./ExtractionStatus";
9
+ import type { BeEvent } from "@itwin/core-bentley";
10
+ import { STATUS_CHECK_INTERVAL } from "./Constants";
11
+ import { ReportsConfigWidget } from "../../ReportsConfigWidget";
12
+ import { IconButton } from "@itwin/itwinui-react";
13
+ import {
14
+ SvgDelete,
15
+ SvgRefresh,
16
+ } from "@itwin/itwinui-icons-react";
17
+ import { HorizontalTile } from "./HorizontalTile";
18
+ import type { ReportMappingAndMapping } from "./ReportMappings";
19
+
20
+ export interface ReportMappingHorizontalTileProps {
21
+ jobStartEvent: BeEvent<(iModelId: string) => void>;
22
+ mapping: ReportMappingAndMapping;
23
+ onClickDelete: () => void;
24
+ bulkExtractor: BulkExtractor;
25
+ odataFeedUrl: string;
26
+ }
27
+
28
+ export const ReportMappingHorizontalTile = (props: ReportMappingHorizontalTileProps) => {
29
+ const [extractionState, setExtractionState] = useState<ExtractionStates>(ExtractionStates.None);
30
+ const [jobStarted, setJobStarted] = useState<boolean>(true);
31
+ const interval = useRef<number>();
32
+ const initialLoad = useRef<boolean>(true);
33
+
34
+ useEffect(() => {
35
+ const listener = (startedIModelId: string) => {
36
+ if (startedIModelId === props.mapping.imodelId) {
37
+ setExtractionState(ExtractionStates.Starting);
38
+ setJobStarted(true);
39
+ }
40
+ };
41
+ props.jobStartEvent.addListener(listener);
42
+
43
+ return () => {
44
+ props.jobStartEvent.removeListener(listener);
45
+ };
46
+ }, [props.jobStartEvent, props.mapping]);
47
+
48
+ const getExtractionState = useCallback(async () => {
49
+ const state = await props.bulkExtractor.getIModelState(props.mapping.imodelId, props.mapping.iModelName, props.odataFeedUrl);
50
+ if (state === ExtractionStates.Failed || state === ExtractionStates.Succeeded || state === ExtractionStates.None) {
51
+ setJobStarted(false);
52
+ if (initialLoad.current) {
53
+ initialLoad.current = false;
54
+ setExtractionState(ExtractionStates.None);
55
+ return;
56
+ }
57
+ } else {
58
+ initialLoad.current = false;
59
+ }
60
+ setExtractionState(state);
61
+ }, [props.mapping, props.bulkExtractor, props.odataFeedUrl]);
62
+
63
+ useEffect(() => {
64
+ if (jobStarted) {
65
+ getExtractionState().catch((error) => {
66
+ setExtractionState(ExtractionStates.Failed);
67
+ setJobStarted(false);
68
+ /* eslint-disable no-console */
69
+ console.error(error);
70
+ });
71
+ window.clearInterval(interval.current);
72
+ interval.current = window.setInterval(async () => {
73
+ await getExtractionState();
74
+ }, STATUS_CHECK_INTERVAL);
75
+ }
76
+ return () => window.clearInterval(interval.current);
77
+ }, [jobStarted, getExtractionState]);
78
+
79
+ return (
80
+ <HorizontalTile
81
+ title={props.mapping.mappingName}
82
+ subText={props.mapping.iModelName}
83
+ titleTooltip={props.mapping.mappingDescription}
84
+ actionGroup={(
85
+ <div
86
+ className="rcw-action-button"
87
+ data-testid="tile-action-button">
88
+ {extractionState === ExtractionStates.None ?
89
+ (
90
+ <IconButton
91
+ styleType="borderless"
92
+ title={ReportsConfigWidget.localization.getLocalizedString(
93
+ "ReportsConfigWidget:UpdateDataset"
94
+ )}
95
+ onClick={async () => {
96
+ setExtractionState(ExtractionStates.Starting);
97
+ await props.bulkExtractor.runIModelExtraction(props.mapping.imodelId);
98
+ props.jobStartEvent.raiseEvent(props.mapping.imodelId);
99
+ }}
100
+ disabled={jobStarted}
101
+ >
102
+ <SvgRefresh />
103
+ </IconButton>
104
+ ) : (
105
+ <ExtractionStatus
106
+ state={extractionState}
107
+ clearExtractionState={() => {
108
+ setExtractionState(ExtractionStates.None);
109
+ }}
110
+ ></ExtractionStatus>
111
+ )}
112
+ <IconButton
113
+ styleType="borderless"
114
+ title={ReportsConfigWidget.localization.getLocalizedString(
115
+ "ReportsConfigWidget:Remove"
116
+ )}
117
+ onClick={() => {
118
+ props.onClickDelete();
119
+ }}
120
+ disabled={jobStarted}
121
+ >
122
+ <SvgDelete />
123
+ </IconButton>
124
+ </div >
125
+ )}
126
+ />
127
+ );
128
+ };