@itwin/reports-config-widget-react 0.0.7 → 0.1.0

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 (174) hide show
  1. package/.rush/temp/package-deps_rebuild.json +24 -14
  2. package/.rush/temp/shrinkwrap-deps.json +17 -17
  3. package/CHANGELOG.json +24 -0
  4. package/CHANGELOG.md +15 -1
  5. package/coverage/clover.xml +354 -128
  6. package/coverage/coverage-final.json +17 -8
  7. package/coverage/lcov-report/index.html +36 -21
  8. package/coverage/lcov-report/src/ReportsConfigWidget.ts.html +2 -2
  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 +1 -1
  12. package/coverage/lcov-report/src/widget/ReportsConfigUiProvider.tsx.html +1 -1
  13. package/coverage/lcov-report/src/widget/components/ActionPanel.tsx.html +1 -1
  14. package/coverage/lcov-report/src/widget/components/AddMappingsModal.tsx.html +1 -1
  15. package/coverage/lcov-report/src/widget/components/BulkExtractor.ts.html +553 -0
  16. package/coverage/lcov-report/src/widget/components/Constants.ts.html +106 -0
  17. package/coverage/lcov-report/src/widget/components/DeleteModal.tsx.html +4 -4
  18. package/coverage/lcov-report/src/widget/components/Extraction.tsx.html +1 -1
  19. package/coverage/lcov-report/src/widget/components/ExtractionStates/FailedExtractionState.tsx.html +187 -0
  20. package/coverage/lcov-report/src/widget/components/ExtractionStates/QueuedExtractionState.tsx.html +145 -0
  21. package/coverage/lcov-report/src/widget/components/ExtractionStates/RunningExtractionState.tsx.html +139 -0
  22. package/coverage/lcov-report/src/widget/components/ExtractionStates/StartingExtractionState.tsx.html +151 -0
  23. package/coverage/lcov-report/src/widget/components/ExtractionStates/SucceededExtractionState.tsx.html +187 -0
  24. package/coverage/lcov-report/src/widget/components/ExtractionStates/index.html +176 -0
  25. package/coverage/lcov-report/src/widget/components/ExtractionStatus.tsx.html +295 -0
  26. package/coverage/lcov-report/src/widget/components/HorizontalTile.tsx.html +73 -40
  27. package/coverage/lcov-report/src/widget/components/LocalizedTablePaginator.tsx.html +1 -1
  28. package/coverage/lcov-report/src/widget/components/ReportAction.tsx.html +1 -1
  29. package/coverage/lcov-report/src/widget/components/ReportHorizontalTile.tsx.html +478 -0
  30. package/coverage/lcov-report/src/widget/components/ReportMappings.tsx.html +6 -24
  31. package/coverage/lcov-report/src/widget/components/Reports.tsx.html +129 -111
  32. package/coverage/lcov-report/src/widget/components/ReportsContainer.tsx.html +1 -1
  33. package/coverage/lcov-report/src/widget/components/SearchBar.tsx.html +5 -5
  34. package/coverage/lcov-report/src/widget/components/SelectIModel.tsx.html +1 -1
  35. package/coverage/lcov-report/src/widget/components/index.html +83 -23
  36. package/coverage/lcov-report/src/widget/components/utils.tsx.html +3 -3
  37. package/coverage/lcov-report/src/widget/context/ReportsApiConfigContext.tsx.html +4 -4
  38. package/coverage/lcov-report/src/widget/context/index.html +1 -1
  39. package/coverage/lcov-report/src/widget/hooks/index.html +1 -1
  40. package/coverage/lcov-report/src/widget/hooks/useValidator.ts.html +1 -1
  41. package/coverage/lcov-report/src/widget/index.html +1 -1
  42. package/coverage/lcov.info +699 -287
  43. package/jest.config.js +1 -0
  44. package/lib/cjs/tsconfig.tsbuildinfo +1 -1
  45. package/lib/cjs/widget/components/BulkExtractor.d.ts +28 -0
  46. package/lib/cjs/widget/components/BulkExtractor.d.ts.map +1 -0
  47. package/lib/cjs/widget/components/BulkExtractor.js +126 -0
  48. package/lib/cjs/widget/components/BulkExtractor.js.map +1 -0
  49. package/lib/cjs/widget/components/Constants.d.ts +4 -0
  50. package/lib/cjs/widget/components/Constants.d.ts.map +1 -0
  51. package/lib/cjs/widget/components/Constants.js +11 -0
  52. package/lib/cjs/widget/components/Constants.js.map +1 -0
  53. package/lib/cjs/widget/components/ExtractionStates/FailedExtractionState.d.ts +8 -0
  54. package/lib/cjs/widget/components/ExtractionStates/FailedExtractionState.d.ts.map +1 -0
  55. package/lib/cjs/widget/components/ExtractionStates/FailedExtractionState.js +23 -0
  56. package/lib/cjs/widget/components/ExtractionStates/FailedExtractionState.js.map +1 -0
  57. package/lib/cjs/widget/components/ExtractionStates/QueuedExtractionState.d.ts +3 -0
  58. package/lib/cjs/widget/components/ExtractionStates/QueuedExtractionState.d.ts.map +1 -0
  59. package/lib/cjs/widget/components/ExtractionStates/QueuedExtractionState.js +18 -0
  60. package/lib/cjs/widget/components/ExtractionStates/QueuedExtractionState.js.map +1 -0
  61. package/lib/cjs/widget/components/ExtractionStates/RunningExtractionState.d.ts +3 -0
  62. package/lib/cjs/widget/components/ExtractionStates/RunningExtractionState.d.ts.map +1 -0
  63. package/lib/cjs/widget/components/ExtractionStates/RunningExtractionState.js +17 -0
  64. package/lib/cjs/widget/components/ExtractionStates/RunningExtractionState.js.map +1 -0
  65. package/lib/cjs/widget/components/ExtractionStates/StartingExtractionState.d.ts +3 -0
  66. package/lib/cjs/widget/components/ExtractionStates/StartingExtractionState.d.ts.map +1 -0
  67. package/lib/cjs/widget/components/ExtractionStates/StartingExtractionState.js +18 -0
  68. package/lib/cjs/widget/components/ExtractionStates/StartingExtractionState.js.map +1 -0
  69. package/lib/cjs/widget/components/ExtractionStates/SucceededExtractionState.d.ts +8 -0
  70. package/lib/cjs/widget/components/ExtractionStates/SucceededExtractionState.d.ts.map +1 -0
  71. package/lib/cjs/widget/components/ExtractionStates/SucceededExtractionState.js +23 -0
  72. package/lib/cjs/widget/components/ExtractionStates/SucceededExtractionState.js.map +1 -0
  73. package/lib/cjs/widget/components/ExtractionStatus.d.ts +17 -0
  74. package/lib/cjs/widget/components/ExtractionStatus.d.ts.map +1 -0
  75. package/lib/cjs/widget/components/ExtractionStatus.js +76 -0
  76. package/lib/cjs/widget/components/ExtractionStatus.js.map +1 -0
  77. package/lib/cjs/widget/components/ExtractionStatus.scss +27 -0
  78. package/lib/cjs/widget/components/HorizontalTile.d.ts +8 -7
  79. package/lib/cjs/widget/components/HorizontalTile.d.ts.map +1 -1
  80. package/lib/cjs/widget/components/HorizontalTile.js +10 -6
  81. package/lib/cjs/widget/components/HorizontalTile.js.map +1 -1
  82. package/lib/cjs/widget/components/HorizontalTile.scss +42 -22
  83. package/lib/cjs/widget/components/ReportHorizontalTile.d.ts +16 -0
  84. package/lib/cjs/widget/components/ReportHorizontalTile.d.ts.map +1 -0
  85. package/lib/cjs/widget/components/ReportHorizontalTile.js +72 -0
  86. package/lib/cjs/widget/components/ReportHorizontalTile.js.map +1 -0
  87. package/lib/cjs/widget/components/ReportMappings.d.ts.map +1 -1
  88. package/lib/cjs/widget/components/ReportMappings.js +1 -1
  89. package/lib/cjs/widget/components/ReportMappings.js.map +1 -1
  90. package/lib/cjs/widget/components/Reports.d.ts.map +1 -1
  91. package/lib/cjs/widget/components/Reports.js +34 -26
  92. package/lib/cjs/widget/components/Reports.js.map +1 -1
  93. package/lib/cjs/widget/components/Reports.scss +7 -3
  94. package/lib/cjs/widget/components/ReportsContainer.scss +3 -0
  95. package/lib/cjs/widget/components/SearchBar.js +1 -1
  96. package/lib/cjs/widget/components/SearchBar.js.map +1 -1
  97. package/lib/cjs/widget/components/SearchBar.scss +6 -0
  98. package/lib/esm/tsconfig.tsbuildinfo +1 -1
  99. package/lib/esm/widget/components/BulkExtractor.d.ts +28 -0
  100. package/lib/esm/widget/components/BulkExtractor.d.ts.map +1 -0
  101. package/lib/esm/widget/components/BulkExtractor.js +123 -0
  102. package/lib/esm/widget/components/BulkExtractor.js.map +1 -0
  103. package/lib/esm/widget/components/Constants.d.ts +4 -0
  104. package/lib/esm/widget/components/Constants.d.ts.map +1 -0
  105. package/lib/esm/widget/components/Constants.js +8 -0
  106. package/lib/esm/widget/components/Constants.js.map +1 -0
  107. package/lib/esm/widget/components/ExtractionStates/FailedExtractionState.d.ts +8 -0
  108. package/lib/esm/widget/components/ExtractionStates/FailedExtractionState.d.ts.map +1 -0
  109. package/lib/esm/widget/components/ExtractionStates/FailedExtractionState.js +16 -0
  110. package/lib/esm/widget/components/ExtractionStates/FailedExtractionState.js.map +1 -0
  111. package/lib/esm/widget/components/ExtractionStates/QueuedExtractionState.d.ts +3 -0
  112. package/lib/esm/widget/components/ExtractionStates/QueuedExtractionState.d.ts.map +1 -0
  113. package/lib/esm/widget/components/ExtractionStates/QueuedExtractionState.js +11 -0
  114. package/lib/esm/widget/components/ExtractionStates/QueuedExtractionState.js.map +1 -0
  115. package/lib/esm/widget/components/ExtractionStates/RunningExtractionState.d.ts +3 -0
  116. package/lib/esm/widget/components/ExtractionStates/RunningExtractionState.d.ts.map +1 -0
  117. package/lib/esm/widget/components/ExtractionStates/RunningExtractionState.js +10 -0
  118. package/lib/esm/widget/components/ExtractionStates/RunningExtractionState.js.map +1 -0
  119. package/lib/esm/widget/components/ExtractionStates/StartingExtractionState.d.ts +3 -0
  120. package/lib/esm/widget/components/ExtractionStates/StartingExtractionState.d.ts.map +1 -0
  121. package/lib/esm/widget/components/ExtractionStates/StartingExtractionState.js +11 -0
  122. package/lib/esm/widget/components/ExtractionStates/StartingExtractionState.js.map +1 -0
  123. package/lib/esm/widget/components/ExtractionStates/SucceededExtractionState.d.ts +8 -0
  124. package/lib/esm/widget/components/ExtractionStates/SucceededExtractionState.d.ts.map +1 -0
  125. package/lib/esm/widget/components/ExtractionStates/SucceededExtractionState.js +16 -0
  126. package/lib/esm/widget/components/ExtractionStates/SucceededExtractionState.js.map +1 -0
  127. package/lib/esm/widget/components/ExtractionStatus.d.ts +17 -0
  128. package/lib/esm/widget/components/ExtractionStatus.d.ts.map +1 -0
  129. package/lib/esm/widget/components/ExtractionStatus.js +53 -0
  130. package/lib/esm/widget/components/ExtractionStatus.js.map +1 -0
  131. package/lib/esm/widget/components/ExtractionStatus.scss +27 -0
  132. package/lib/esm/widget/components/HorizontalTile.d.ts +8 -7
  133. package/lib/esm/widget/components/HorizontalTile.d.ts.map +1 -1
  134. package/lib/esm/widget/components/HorizontalTile.js +10 -6
  135. package/lib/esm/widget/components/HorizontalTile.js.map +1 -1
  136. package/lib/esm/widget/components/HorizontalTile.scss +42 -22
  137. package/lib/esm/widget/components/ReportHorizontalTile.d.ts +16 -0
  138. package/lib/esm/widget/components/ReportHorizontalTile.d.ts.map +1 -0
  139. package/lib/esm/widget/components/ReportHorizontalTile.js +65 -0
  140. package/lib/esm/widget/components/ReportHorizontalTile.js.map +1 -0
  141. package/lib/esm/widget/components/ReportMappings.d.ts.map +1 -1
  142. package/lib/esm/widget/components/ReportMappings.js +1 -1
  143. package/lib/esm/widget/components/ReportMappings.js.map +1 -1
  144. package/lib/esm/widget/components/Reports.d.ts.map +1 -1
  145. package/lib/esm/widget/components/Reports.js +36 -28
  146. package/lib/esm/widget/components/Reports.js.map +1 -1
  147. package/lib/esm/widget/components/Reports.scss +7 -3
  148. package/lib/esm/widget/components/ReportsContainer.scss +3 -0
  149. package/lib/esm/widget/components/SearchBar.js +1 -1
  150. package/lib/esm/widget/components/SearchBar.js.map +1 -1
  151. package/lib/esm/widget/components/SearchBar.scss +6 -0
  152. package/lib/public/locales/en/ReportsConfigWidget.json +2 -1
  153. package/package.json +5 -2
  154. package/public/locales/en/ReportsConfigWidget.json +2 -1
  155. package/reports-config-widget-react.build.error.log +4 -4
  156. package/reports-config-widget-react.build.log +44 -34
  157. package/src/widget/components/BulkExtractor.ts +156 -0
  158. package/src/widget/components/Constants.ts +7 -0
  159. package/src/widget/components/ExtractionStates/FailedExtractionState.tsx +34 -0
  160. package/src/widget/components/ExtractionStates/QueuedExtractionState.tsx +20 -0
  161. package/src/widget/components/ExtractionStates/RunningExtractionState.tsx +18 -0
  162. package/src/widget/components/ExtractionStates/StartingExtractionState.tsx +22 -0
  163. package/src/widget/components/ExtractionStates/SucceededExtractionState.tsx +34 -0
  164. package/src/widget/components/ExtractionStatus.scss +27 -0
  165. package/src/widget/components/ExtractionStatus.tsx +70 -0
  166. package/src/widget/components/HorizontalTile.scss +42 -22
  167. package/src/widget/components/HorizontalTile.tsx +45 -34
  168. package/src/widget/components/ReportHorizontalTile.tsx +131 -0
  169. package/src/widget/components/ReportMappings.tsx +4 -10
  170. package/src/widget/components/Reports.scss +7 -3
  171. package/src/widget/components/Reports.tsx +66 -60
  172. package/src/widget/components/ReportsContainer.scss +3 -0
  173. package/src/widget/components/SearchBar.scss +6 -0
  174. package/src/widget/components/SearchBar.tsx +1 -1
@@ -6,51 +6,62 @@ import type { ReactNode } from "react";
6
6
  import React from "react";
7
7
  import { Text } from "@itwin/itwinui-react";
8
8
  import "./HorizontalTile.scss";
9
+ import classNames from "classnames";
9
10
 
10
- interface HorizontalTileProps {
11
+ export interface HorizontalTileProps extends React.ComponentPropsWithoutRef<"div"> {
11
12
  title: string;
12
- button: ReactNode;
13
- subText?: string;
14
- onClickTitle?: () => void;
15
- titleTooltip?: string;
13
+ actionGroup: ReactNode;
14
+ subText: string;
15
+ onClickTitle?: React.MouseEventHandler;
16
+ titleTooltip: string;
16
17
  subtextToolTip?: string;
18
+ selected?: boolean;
17
19
  }
18
20
 
19
- export const HorizontalTile = ({
20
- title,
21
- subText,
22
- onClickTitle,
23
- titleTooltip,
24
- subtextToolTip,
25
- button,
26
- }: HorizontalTileProps) => {
21
+ export const HorizontalTile = (props: HorizontalTileProps) => {
22
+ const {
23
+ title,
24
+ titleTooltip,
25
+ subText,
26
+ subtextToolTip,
27
+ actionGroup,
28
+ selected,
29
+ className,
30
+ onClickTitle,
31
+ ...rest
32
+ } = props;
33
+
27
34
  return (
28
35
  <div
29
- className="rcw-horizontal-tile-container"
36
+ className={classNames(
37
+ "rcw-horizontal-tile-container",
38
+ { "rcw-horizontal-tile-selected": selected },
39
+ className
40
+ )}
41
+ onClick={rest.onClick}
30
42
  data-testid="horizontal-tile"
31
43
  >
32
- <div className="body">
33
- <Text
34
- className={`body-text ${onClickTitle ? "iui-anchor" : ""}`}
35
- onClick={onClickTitle}
36
- variant="body"
37
- title={titleTooltip}
38
- >
39
- {title}
40
- </Text>
41
- {subText && (
42
- <Text
43
- className="body-text"
44
- isMuted={true}
45
- title={subtextToolTip}
46
- variant="small"
47
- >
48
- {subText}
44
+ <div className="rcw-body-container">
45
+ <div className="rcw-body">
46
+ <Text className={classNames("rcw-body-text", { "iui-anchor": !!onClickTitle })}
47
+ onClick={onClickTitle}
48
+ variant="body"
49
+ title={titleTooltip}>{title}
49
50
  </Text>
50
- )}
51
+ {
52
+ subText &&
53
+ <Text className="rcw-body-text"
54
+ isMuted={true}
55
+ title={subtextToolTip}
56
+ variant="small">{subText}
57
+ </Text>
58
+ }
59
+ </div>
51
60
  </div>
52
- <div className="action-button" data-testid="tile-action-button">
53
- {button}
61
+ <div
62
+ className="rcw-action-button"
63
+ data-testid="tile-action-button">
64
+ {actionGroup}
54
65
  </div>
55
66
  </div>
56
67
  );
@@ -0,0 +1,131 @@
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 { 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 type { Report } from "@itwin/insights-client";
12
+ import { ReportsConfigWidget } from "../../ReportsConfigWidget";
13
+ import {
14
+ DropdownMenu,
15
+ IconButton,
16
+ MenuItem,
17
+ } from "@itwin/itwinui-react";
18
+ import {
19
+ SvgDelete,
20
+ SvgEdit,
21
+ SvgMore,
22
+ } from "@itwin/itwinui-icons-react";
23
+ import { HorizontalTile } from "./HorizontalTile";
24
+ import type { HorizontalTileProps } from "./HorizontalTile";
25
+
26
+ export interface ReportHorizontalTileProps extends Pick<HorizontalTileProps, "onClickTitle"> {
27
+ selected: boolean;
28
+ onSelectionChange: (reportId: string, controlPressed: boolean) => void;
29
+ bulkExtractor: BulkExtractor;
30
+ jobStartEvent: BeEvent<(reportId: string) => void>;
31
+ report: Report;
32
+ onClickDelete: () => void;
33
+ onClickModify: () => void;
34
+ }
35
+
36
+ export const ReportHorizontalTile = (props: ReportHorizontalTileProps) => {
37
+ const [jobStarted, setJobStarted] = useState<boolean>(false);
38
+ const [extractionState, setExtractionState] = useState<ExtractionStates>(ExtractionStates.None);
39
+ const interval = useRef<number>();
40
+
41
+ useEffect(() => {
42
+ const listener = (startedReportId: string) => {
43
+ if (startedReportId === props.report.id) {
44
+ setExtractionState(ExtractionStates.Starting);
45
+ setJobStarted(true);
46
+ }
47
+ };
48
+ props.jobStartEvent.addListener(listener);
49
+
50
+ return () => {
51
+ props.jobStartEvent.removeListener(listener);
52
+ };
53
+ }, [props.jobStartEvent, props.report.id]);
54
+
55
+ useEffect(() => {
56
+ if (jobStarted) {
57
+ window.clearInterval(interval.current);
58
+ interval.current = window.setInterval(async () => {
59
+ const state = props.bulkExtractor.getState(props.report.id);
60
+ if (state) {
61
+ setExtractionState(state);
62
+ if (state === ExtractionStates.Failed || state === ExtractionStates.Succeeded) {
63
+ setJobStarted(false);
64
+ }
65
+ }
66
+ }, STATUS_CHECK_INTERVAL);
67
+ }
68
+ return () => window.clearInterval(interval.current);
69
+ }, [props.report.id, props.bulkExtractor, jobStarted]);
70
+
71
+ const onClickTile = (e: React.MouseEvent) => {
72
+ if (e?.currentTarget.className?.toString().split(" ").includes("rcw-horizontal-tile-container")) {
73
+ props.onSelectionChange(props.report.id, e.ctrlKey);
74
+ }
75
+ };
76
+
77
+ return (
78
+ <HorizontalTile
79
+ title={props.report.displayName}
80
+ subText={props.report.description ?? ""}
81
+ subtextToolTip={props.report.description ?? ""}
82
+ titleTooltip={props.report.displayName}
83
+ onClick={onClickTile}
84
+ onClickTitle={props.onClickTitle}
85
+ selected={props.selected}
86
+ actionGroup={extractionState === ExtractionStates.None ? (
87
+ <div
88
+ className="rcw-action-button"
89
+ data-testid="tile-action-button">
90
+ <DropdownMenu
91
+ menuItems={(close: () => void) => [
92
+ <MenuItem
93
+ key={0}
94
+ onClick={props.onClickModify}
95
+ icon={<SvgEdit />}
96
+ >
97
+ {ReportsConfigWidget.localization.getLocalizedString(
98
+ "ReportsConfigWidget:Modify"
99
+ )}
100
+ </MenuItem>,
101
+ <MenuItem
102
+ key={1}
103
+ onClick={() => {
104
+ props.onClickDelete();
105
+ close();
106
+ }}
107
+ icon={<SvgDelete />}
108
+ >
109
+ {ReportsConfigWidget.localization.getLocalizedString(
110
+ "ReportsConfigWidget:Remove"
111
+ )}
112
+ </MenuItem>,
113
+ ]}
114
+ >
115
+ <IconButton styleType="borderless">
116
+ <SvgMore />
117
+ </IconButton>
118
+ </DropdownMenu>
119
+ </div>
120
+ ) : (
121
+ <ExtractionStatus
122
+ state={extractionState}
123
+ clearExtractionState={() => {
124
+ props.bulkExtractor.clearJob(props.report.id);
125
+ setExtractionState(ExtractionStates.None);
126
+ }}
127
+ ></ExtractionStatus>
128
+ )}
129
+ ></HorizontalTile>
130
+ );
131
+ };
@@ -59,9 +59,7 @@ enum ReportMappingsView {
59
59
  }
60
60
 
61
61
  const fetchReportMappings = async (
62
- setReportMappings: React.Dispatch<
63
- React.SetStateAction<ReportMappingAndMapping[]>
64
- >,
62
+ setReportMappings: (mappings: ReportMappingAndMapping[]) => void,
65
63
  reportId: string,
66
64
  setIsLoading: React.Dispatch<React.SetStateAction<boolean>>,
67
65
  apiContext: ReportsApiConfig
@@ -134,9 +132,7 @@ export const ReportMappings = ({ report, goBack }: ReportMappingsProps) => {
134
132
  const apiConfig = useReportsApiConfig();
135
133
  const [reportMappingsView, setReportMappingsView] =
136
134
  useState<ReportMappingsView>(ReportMappingsView.REPORTMAPPINGS);
137
- const [selectedReportMapping, setSelectedReportMapping] = useState<
138
- ReportMappingAndMapping | undefined
139
- >(undefined);
135
+ const [selectedReportMapping, setSelectedReportMapping] = useState<ReportMappingAndMapping | undefined>(undefined);
140
136
  const [showDeleteModal, setShowDeleteModal] = useState<boolean>(false);
141
137
  const [isLoading, setIsLoading] = useState<boolean>(true);
142
138
 
@@ -145,9 +141,7 @@ export const ReportMappings = ({ report, goBack }: ReportMappingsProps) => {
145
141
  );
146
142
  const [runningIModelId, setRunningIModelId] = useState<string>("");
147
143
  const [searchValue, setSearchValue] = useState<string>("");
148
- const [reportMappings, setReportMappings] = useState<
149
- ReportMappingAndMapping[]
150
- >([]);
144
+ const [reportMappings, setReportMappings] = useState<ReportMappingAndMapping[]>([]);
151
145
 
152
146
  useEffect(() => {
153
147
  void fetchReportMappings(
@@ -284,7 +278,7 @@ export const ReportMappings = ({ report, goBack }: ReportMappingsProps) => {
284
278
  title={mapping.mappingName}
285
279
  subText={mapping.iModelName}
286
280
  titleTooltip={mapping.mappingDescription}
287
- button={
281
+ actionGroup={
288
282
  <ExtractionStatus
289
283
  state={
290
284
  mapping.imodelId === runningIModelId
@@ -11,7 +11,7 @@
11
11
  padding: $iui-baseline $iui-m;
12
12
  min-height: 0;
13
13
 
14
- .toolbar {
14
+ .rcw-toolbar {
15
15
  display: flex;
16
16
  justify-content: space-between;
17
17
  gap: $iui-s;
@@ -20,13 +20,13 @@
20
20
  flex-wrap: wrap;
21
21
  }
22
22
 
23
- .search-bar-container {
23
+ .rcw-search-bar-container {
24
24
  flex-basis: $iui-3xl;
25
25
  flex-shrink: 1;
26
26
  flex-grow: 1;
27
27
  }
28
28
 
29
- .reports-list {
29
+ .rcw-reports-list {
30
30
  display: flex;
31
31
  flex-direction: column;
32
32
  overflow-y: overlay;
@@ -34,3 +34,7 @@
34
34
  margin-top: 7.5px;
35
35
  }
36
36
  }
37
+
38
+ .rcw-button-container {
39
+ display: flex;
40
+ }
@@ -4,15 +4,11 @@
4
4
  *--------------------------------------------------------------------------------------------*/
5
5
  import {
6
6
  SvgAdd,
7
- SvgDelete,
8
- SvgEdit,
9
- SvgMore,
7
+ SvgRefresh,
10
8
  } from "@itwin/itwinui-icons-react";
11
9
  import {
12
10
  Button,
13
- DropdownMenu,
14
11
  IconButton,
15
- MenuItem,
16
12
  Surface,
17
13
  } from "@itwin/itwinui-react";
18
14
  import React, { useCallback, useEffect, useMemo, useState } from "react";
@@ -30,12 +26,14 @@ import type { Report } from "@itwin/insights-client";
30
26
  import { REPORTING_BASE_PATH, ReportsClient } from "@itwin/insights-client";
31
27
  import ReportAction from "./ReportAction";
32
28
  import { ReportMappings } from "./ReportMappings";
33
- import { HorizontalTile } from "./HorizontalTile";
29
+ import { ReportHorizontalTile } from "./ReportHorizontalTile";
34
30
  import { SearchBar } from "./SearchBar";
35
31
  import type { ReportsApiConfig } from "../context/ReportsApiConfigContext";
36
32
  import { useReportsApiConfig } from "../context/ReportsApiConfigContext";
37
33
  import { ReportsConfigWidget } from "../../ReportsConfigWidget";
38
34
  import { useActiveIModelConnection } from "@itwin/appui-react";
35
+ import BulkExtractor from "./BulkExtractor";
36
+ import { BeEvent } from "@itwin/core-bentley";
39
37
 
40
38
  export type ReportType = CreateTypeFromInterface<Report>;
41
39
 
@@ -71,6 +69,7 @@ const fetchReports = async (
71
69
  export const Reports = () => {
72
70
  const iTwinId = useActiveIModelConnection()?.iTwinId ?? "";
73
71
  const apiConfig = useReportsApiConfig();
72
+ const [selectedReportIds, setSelectedReportIds] = useState<string[]>([]);
74
73
  const [showDeleteModal, setShowDeleteModal] = useState<boolean>(false);
75
74
  const [reportsView, setReportsView] = useState<ReportsView>(
76
75
  ReportsView.REPORTS
@@ -81,6 +80,14 @@ export const Reports = () => {
81
80
  const [isLoading, setIsLoading] = useState<boolean>(true);
82
81
  const [searchValue, setSearchValue] = useState<string>("");
83
82
  const [reports, setReports] = useState<Report[]>([]);
83
+ const bulkExtractor = useMemo(
84
+ () => new BulkExtractor(apiConfig, reports.map((r) => r.id)),
85
+ [apiConfig, reports]
86
+ );
87
+ const jobStartEvent = useMemo(
88
+ () => new BeEvent<(reportId: string) => void>(),
89
+ []
90
+ );
84
91
 
85
92
  useEffect(() => {
86
93
  void fetchReports(setReports, iTwinId, setIsLoading, apiConfig);
@@ -107,6 +114,25 @@ export const Reports = () => {
107
114
  [reports, searchValue]
108
115
  );
109
116
 
117
+ const onSelectionChange = (reportId: string, control: boolean) => {
118
+ if (!control)
119
+ setSelectedReportIds([]);
120
+
121
+ setSelectedReportIds((sr) =>
122
+ sr.some((r) => reportId === r)
123
+ ? sr.filter(
124
+ (r) => reportId !== r
125
+ )
126
+ : [...sr, reportId]
127
+ );
128
+ };
129
+
130
+ const updateDatasets = useCallback(async () => {
131
+ selectedReportIds.map((reportId) => jobStartEvent.raiseEvent(reportId));
132
+ setSelectedReportIds([]);
133
+ await bulkExtractor.startJobs(selectedReportIds);
134
+ }, [selectedReportIds, jobStartEvent, bulkExtractor]);
135
+
110
136
  switch (reportsView) {
111
137
  case ReportsView.ADDING:
112
138
  return iTwinId ? (
@@ -133,7 +159,7 @@ export const Reports = () => {
133
159
  )}
134
160
  />
135
161
  <Surface className="reports-list-container">
136
- <div className="toolbar">
162
+ <div className="rcw-toolbar">
137
163
  <Button
138
164
  startIcon={<SvgAdd />}
139
165
  onClick={() => addReport()}
@@ -143,12 +169,23 @@ export const Reports = () => {
143
169
  "ReportsConfigWidget:New"
144
170
  )}
145
171
  </Button>
146
- <div className="search-bar-container" data-testid="search-bar">
147
- <SearchBar
148
- searchValue={searchValue}
149
- setSearchValue={setSearchValue}
150
- disabled={isLoading}
151
- />
172
+ <IconButton
173
+ title={ReportsConfigWidget.localization.getLocalizedString(
174
+ "ReportsConfigWidget:UpdateDatasets"
175
+ )}
176
+ onClick={updateDatasets}
177
+ disabled={selectedReportIds.length === 0}
178
+ >
179
+ <SvgRefresh />
180
+ </IconButton>
181
+ <div className="rcw-search-bar-container" data-testid="search-bar">
182
+ <div className="rcw-search-button">
183
+ <SearchBar
184
+ searchValue={searchValue}
185
+ setSearchValue={setSearchValue}
186
+ disabled={isLoading}
187
+ />
188
+ </div>
152
189
  </div>
153
190
  </div>
154
191
  {isLoading ? (
@@ -160,7 +197,7 @@ export const Reports = () => {
160
197
  "ReportsConfigWidget:NoReports"
161
198
  )}
162
199
  <div>
163
- <Button onClick={() => addReport()} styleType="cta">
200
+ <Button onClick={addReport} styleType="cta">
164
201
  {ReportsConfigWidget.localization.getLocalizedString(
165
202
  "ReportsConfigWidget:CreateOneReportCTA"
166
203
  )}
@@ -169,58 +206,27 @@ export const Reports = () => {
169
206
  </>
170
207
  </EmptyMessage>
171
208
  ) : (
172
- <div className="reports-list">
209
+ <div className="rcw-reports-list">
173
210
  {filteredReports.map((report) => (
174
- <HorizontalTile
211
+ <ReportHorizontalTile
175
212
  key={report.id}
176
- title={report.displayName}
177
- subText={report.description ?? ""}
178
- subtextToolTip={report.description ?? ""}
179
- titleTooltip={report.displayName}
213
+ report={report}
180
214
  onClickTitle={() => {
181
215
  setSelectedReport(report);
182
216
  setReportsView(ReportsView.REPORTSMAPPING);
183
217
  }}
184
- button={
185
- <DropdownMenu
186
- menuItems={(close: () => void) => [
187
- <MenuItem
188
- key={0}
189
- onClick={() => {
190
- setSelectedReport(report);
191
- setReportsView(ReportsView.MODIFYING);
192
- }}
193
- icon={<SvgEdit />}
194
- >
195
- {ReportsConfigWidget.localization.getLocalizedString(
196
- "ReportsConfigWidget:Modify"
197
- )}
198
- </MenuItem>,
199
- <MenuItem
200
- key={1}
201
- onClick={() => {
202
- setSelectedReport(report);
203
- setShowDeleteModal(true);
204
- close();
205
- }}
206
- icon={<SvgDelete />}
207
- >
208
- {ReportsConfigWidget.localization.getLocalizedString(
209
- "ReportsConfigWidget:Remove"
210
- )}
211
- </MenuItem>,
212
- ]}
213
- >
214
- <IconButton styleType="borderless">
215
- <SvgMore
216
- style={{
217
- width: "16px",
218
- height: "16px",
219
- }}
220
- />
221
- </IconButton>
222
- </DropdownMenu>
223
- }
218
+ jobStartEvent={jobStartEvent}
219
+ bulkExtractor={bulkExtractor}
220
+ onClickDelete={() => {
221
+ setSelectedReport(report);
222
+ setShowDeleteModal(true);
223
+ }}
224
+ onClickModify={() => {
225
+ setSelectedReport(report);
226
+ setReportsView(ReportsView.MODIFYING);
227
+ }}
228
+ onSelectionChange={onSelectionChange}
229
+ selected={selectedReportIds.some((reportId) => report.id === reportId)}
224
230
  />
225
231
  ))}
226
232
  </div>
@@ -5,6 +5,9 @@
5
5
  @import "~@itwin/itwinui-css/scss/variables";
6
6
 
7
7
  .reports-container {
8
+ position: absolute;
9
+ right: 0;
10
+ left: 0;
8
11
  display: flex;
9
12
  flex-direction: column;
10
13
  padding: $iui-baseline $iui-m;
@@ -9,6 +9,7 @@
9
9
  opacity: 0;
10
10
  width: 20%;
11
11
  }
12
+
12
13
  100% {
13
14
  opacity: 1;
14
15
  width: 100%;
@@ -20,8 +21,13 @@
20
21
  opacity: 0;
21
22
  width: 20%;
22
23
  }
24
+
23
25
  0% {
24
26
  opacity: 1;
25
27
  width: 100%;
26
28
  }
27
29
  }
30
+
31
+ .rcw-search-button {
32
+ float: right;
33
+ }
@@ -23,7 +23,7 @@ export const SearchBar = ({
23
23
  const [searchBarClosing, setSearchBarClosing] = useState<boolean>(false);
24
24
 
25
25
  return searchBarOpen || searchValue ? (
26
- <div
26
+ <div className="rcw-search-button"
27
27
  style={{
28
28
  animation: searchBarClosing ? "rcw-shrink .5s" : "rcw-expand .5s",
29
29
  }}