@gen3/frontend 0.10.95 → 0.10.97

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 (203) hide show
  1. package/dist/cjs/index.js +1698 -587
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/dts/components/Content/LandingPageContent.d.ts +2 -1
  4. package/dist/dts/components/Content/LandingPageContent.d.ts.map +1 -1
  5. package/dist/dts/components/Content/LandingPageContent.js.map +1 -1
  6. package/dist/dts/components/Login/LoginPanel.d.ts.map +1 -1
  7. package/dist/dts/components/Login/LoginPanel.js +1 -0
  8. package/dist/dts/components/Login/LoginPanel.js.map +1 -1
  9. package/dist/dts/components/Login/types.d.ts +2 -1
  10. package/dist/dts/components/Login/types.d.ts.map +1 -1
  11. package/dist/dts/components/Login/types.js.map +1 -1
  12. package/dist/dts/components/MessageCards/index.d.ts +2 -1
  13. package/dist/dts/components/MessageCards/index.d.ts.map +1 -1
  14. package/dist/dts/components/MessageCards/index.js +2 -1
  15. package/dist/dts/components/MessageCards/index.js.map +1 -1
  16. package/dist/dts/components/Profile/types.d.ts +2 -1
  17. package/dist/dts/components/Profile/types.d.ts.map +1 -1
  18. package/dist/dts/components/charts/Charts.d.ts.map +1 -1
  19. package/dist/dts/components/charts/Charts.js +2 -0
  20. package/dist/dts/components/charts/Charts.js.map +1 -1
  21. package/dist/dts/components/charts/echarts/HorizontalBarChart.d.ts +1 -1
  22. package/dist/dts/components/charts/echarts/HorizontalBarChart.d.ts.map +1 -1
  23. package/dist/dts/components/charts/echarts/HorizontalBarChart.js +3 -2
  24. package/dist/dts/components/charts/echarts/HorizontalBarChart.js.map +1 -1
  25. package/dist/dts/components/charts/types.d.ts +2 -0
  26. package/dist/dts/components/charts/types.d.ts.map +1 -1
  27. package/dist/dts/components/facets/FacetControlsHeader.d.ts.map +1 -1
  28. package/dist/dts/components/facets/FacetControlsHeader.js +1 -1
  29. package/dist/dts/components/facets/FacetControlsHeader.js.map +1 -1
  30. package/dist/dts/components/facets/FacetSelector.d.ts.map +1 -1
  31. package/dist/dts/components/facets/FacetSelector.js +6 -6
  32. package/dist/dts/components/facets/FacetSelector.js.map +1 -1
  33. package/dist/dts/features/Analysis/AnalysisCardCompact.stories.d.ts +6 -6
  34. package/dist/dts/features/Analysis/types.d.ts +2 -1
  35. package/dist/dts/features/Analysis/types.d.ts.map +1 -1
  36. package/dist/dts/features/Analysis/types.js.map +1 -1
  37. package/dist/dts/features/CohortBuilder/CohortBuilder.d.ts.map +1 -1
  38. package/dist/dts/features/CohortBuilder/CohortBuilder.js +1 -2
  39. package/dist/dts/features/CohortBuilder/CohortBuilder.js.map +1 -1
  40. package/dist/dts/features/CohortBuilder/ExplorerTable/ArrayCellRenderer.d.ts +1 -1
  41. package/dist/dts/features/CohortBuilder/QueryExpression/QueryExpressionSection.js +1 -1
  42. package/dist/dts/features/CohortBuilder/QueryExpression/QueryExpressionSection.js.map +1 -1
  43. package/dist/dts/features/CohortBuilder/types.d.ts +2 -2
  44. package/dist/dts/features/CohortBuilder/types.d.ts.map +1 -1
  45. package/dist/dts/features/CohortDiscovery/ActionButtons/ActionButtonGroup.d.ts.map +1 -1
  46. package/dist/dts/features/CohortDiscovery/ActionButtons/ActionButtonGroup.js +18 -2
  47. package/dist/dts/features/CohortDiscovery/ActionButtons/ActionButtonGroup.js.map +1 -1
  48. package/dist/dts/features/CohortDiscovery/ActionButtons/SaveActionButton.d.ts.map +1 -1
  49. package/dist/dts/features/CohortDiscovery/ActionButtons/SaveActionButton.js +10 -4
  50. package/dist/dts/features/CohortDiscovery/ActionButtons/SaveActionButton.js.map +1 -1
  51. package/dist/dts/features/CohortDiscovery/ChartsAndFacetsPanel.js +2 -2
  52. package/dist/dts/features/CohortDiscovery/ChartsAndFacetsPanel.js.map +1 -1
  53. package/dist/dts/features/CohortDiscovery/CohortDiscovery.d.ts.map +1 -1
  54. package/dist/dts/features/CohortDiscovery/CohortDiscovery.js +8 -3
  55. package/dist/dts/features/CohortDiscovery/CohortDiscovery.js.map +1 -1
  56. package/dist/dts/features/CohortDiscovery/CohortManagment/CohortManager.d.ts +5 -2
  57. package/dist/dts/features/CohortDiscovery/CohortManagment/CohortManager.d.ts.map +1 -1
  58. package/dist/dts/features/CohortDiscovery/CohortManagment/CohortManager.js +120 -44
  59. package/dist/dts/features/CohortDiscovery/CohortManagment/CohortManager.js.map +1 -1
  60. package/dist/dts/features/CohortDiscovery/CohortManagment/CohortManagerSelectors.d.ts +44 -0
  61. package/dist/dts/features/CohortDiscovery/CohortManagment/CohortManagerSelectors.d.ts.map +1 -1
  62. package/dist/dts/features/CohortDiscovery/CohortManagment/CohortManagerSelectors.js +7 -1
  63. package/dist/dts/features/CohortDiscovery/CohortManagment/CohortManagerSelectors.js.map +1 -1
  64. package/dist/dts/features/CohortDiscovery/CohortManagment/CohortManagerSlice.d.ts +3 -1
  65. package/dist/dts/features/CohortDiscovery/CohortManagment/CohortManagerSlice.d.ts.map +1 -1
  66. package/dist/dts/features/CohortDiscovery/CohortManagment/CohortManagerSlice.js +71 -24
  67. package/dist/dts/features/CohortDiscovery/CohortManagment/CohortManagerSlice.js.map +1 -1
  68. package/dist/dts/features/CohortDiscovery/CohortManagment/CohortStorage.d.ts.map +1 -1
  69. package/dist/dts/features/CohortDiscovery/CohortManagment/CohortStorage.js +0 -2
  70. package/dist/dts/features/CohortDiscovery/CohortManagment/CohortStorage.js.map +1 -1
  71. package/dist/dts/features/CohortDiscovery/CohortQueryExpression.d.ts +6 -0
  72. package/dist/dts/features/CohortDiscovery/CohortQueryExpression.d.ts.map +1 -0
  73. package/dist/dts/features/CohortDiscovery/CohortQueryExpression.js +55 -0
  74. package/dist/dts/features/CohortDiscovery/CohortQueryExpression.js.map +1 -0
  75. package/dist/dts/features/CohortDiscovery/CohortSelectors.d.ts +0 -22
  76. package/dist/dts/features/CohortDiscovery/CohortSelectors.d.ts.map +1 -1
  77. package/dist/dts/features/CohortDiscovery/CohortSelectors.js +40 -34
  78. package/dist/dts/features/CohortDiscovery/CohortSelectors.js.map +1 -1
  79. package/dist/dts/features/CohortDiscovery/CohortSlice.d.ts +0 -90
  80. package/dist/dts/features/CohortDiscovery/CohortSlice.d.ts.map +1 -1
  81. package/dist/dts/features/CohortDiscovery/CohortSlice.js +161 -92
  82. package/dist/dts/features/CohortDiscovery/CohortSlice.js.map +1 -1
  83. package/dist/dts/features/CohortDiscovery/FacetSelectionPanel.d.ts.map +1 -1
  84. package/dist/dts/features/CohortDiscovery/FacetSelectionPanel.js +1 -1
  85. package/dist/dts/features/CohortDiscovery/FacetSelectionPanel.js.map +1 -1
  86. package/dist/dts/features/CohortDiscovery/FilterPanels/EnumFacetPanel.d.ts.map +1 -1
  87. package/dist/dts/features/CohortDiscovery/FilterPanels/EnumFacetPanel.js +5 -3
  88. package/dist/dts/features/CohortDiscovery/FilterPanels/EnumFacetPanel.js.map +1 -1
  89. package/dist/dts/features/CohortDiscovery/IndexPanel.d.ts +1 -1
  90. package/dist/dts/features/CohortDiscovery/IndexPanel.d.ts.map +1 -1
  91. package/dist/dts/features/CohortDiscovery/IndexPanel.js +8 -8
  92. package/dist/dts/features/CohortDiscovery/IndexPanel.js.map +1 -1
  93. package/dist/dts/features/CohortDiscovery/RequestManagerSlice.d.ts +14 -28
  94. package/dist/dts/features/CohortDiscovery/RequestManagerSlice.d.ts.map +1 -1
  95. package/dist/dts/features/CohortDiscovery/RequestManagerSlice.js +4 -4
  96. package/dist/dts/features/CohortDiscovery/RequestManagerSlice.js.map +1 -1
  97. package/dist/dts/features/CohortDiscovery/Requests/DataAccessRequestForm.d.ts.map +1 -1
  98. package/dist/dts/features/CohortDiscovery/Requests/DataAccessRequestForm.js +4 -10
  99. package/dist/dts/features/CohortDiscovery/Requests/DataAccessRequestForm.js.map +1 -1
  100. package/dist/dts/features/CohortDiscovery/Requests/RequestsPanel.d.ts.map +1 -1
  101. package/dist/dts/features/CohortDiscovery/Requests/RequestsPanel.js +2 -1
  102. package/dist/dts/features/CohortDiscovery/Requests/RequestsPanel.js.map +1 -1
  103. package/dist/dts/features/CohortDiscovery/Requests/RequestsTable.d.ts.map +1 -1
  104. package/dist/dts/features/CohortDiscovery/Requests/RequestsTable.js +76 -13
  105. package/dist/dts/features/CohortDiscovery/Requests/RequestsTable.js.map +1 -1
  106. package/dist/dts/features/CohortDiscovery/Requests/hooks.d.ts +3 -3
  107. package/dist/dts/features/CohortDiscovery/Requests/hooks.d.ts.map +1 -1
  108. package/dist/dts/features/CohortDiscovery/Requests/hooks.js.map +1 -1
  109. package/dist/dts/features/CohortDiscovery/Requests/submitCohortRequestAction.d.ts +9 -0
  110. package/dist/dts/features/CohortDiscovery/Requests/submitCohortRequestAction.d.ts.map +1 -0
  111. package/dist/dts/features/CohortDiscovery/Requests/submitCohortRequestAction.js +133 -0
  112. package/dist/dts/features/CohortDiscovery/Requests/submitCohortRequestAction.js.map +1 -0
  113. package/dist/dts/features/CohortDiscovery/SavedCohorts/SavedCohortsTable.d.ts.map +1 -1
  114. package/dist/dts/features/CohortDiscovery/SavedCohorts/SavedCohortsTable.js +25 -99
  115. package/dist/dts/features/CohortDiscovery/SavedCohorts/SavedCohortsTable.js.map +1 -1
  116. package/dist/dts/features/CohortDiscovery/TabbedIndex.d.ts +3 -2
  117. package/dist/dts/features/CohortDiscovery/TabbedIndex.d.ts.map +1 -1
  118. package/dist/dts/features/CohortDiscovery/TabbedIndex.js +2 -2
  119. package/dist/dts/features/CohortDiscovery/TabbedIndex.js.map +1 -1
  120. package/dist/dts/features/CohortDiscovery/appApi.d.ts +2 -4
  121. package/dist/dts/features/CohortDiscovery/appApi.d.ts.map +1 -1
  122. package/dist/dts/features/CohortDiscovery/appApi.js +6 -4
  123. package/dist/dts/features/CohortDiscovery/appApi.js.map +1 -1
  124. package/dist/dts/features/CohortDiscovery/hooks.d.ts.map +1 -1
  125. package/dist/dts/features/CohortDiscovery/hooks.js +7 -9
  126. package/dist/dts/features/CohortDiscovery/hooks.js.map +1 -1
  127. package/dist/dts/features/CohortDiscovery/queryApi.d.ts.map +1 -1
  128. package/dist/dts/features/CohortDiscovery/queryApi.js.map +1 -1
  129. package/dist/dts/features/CohortDiscovery/types.d.ts +47 -19
  130. package/dist/dts/features/CohortDiscovery/types.d.ts.map +1 -1
  131. package/dist/dts/features/CohortDiscovery/types.js +16 -6
  132. package/dist/dts/features/CohortDiscovery/types.js.map +1 -1
  133. package/dist/dts/features/CohortDiscovery/utils.d.ts +2 -0
  134. package/dist/dts/features/CohortDiscovery/utils.d.ts.map +1 -1
  135. package/dist/dts/features/CohortDiscovery/utils.js +9 -0
  136. package/dist/dts/features/CohortDiscovery/utils.js.map +1 -1
  137. package/dist/dts/features/Crosswalk/types.d.ts +2 -1
  138. package/dist/dts/features/Crosswalk/types.d.ts.map +1 -1
  139. package/dist/dts/features/Dictionary/types.d.ts +5 -1
  140. package/dist/dts/features/Dictionary/types.d.ts.map +1 -1
  141. package/dist/dts/features/Discovery/types.d.ts +2 -1
  142. package/dist/dts/features/Discovery/types.d.ts.map +1 -1
  143. package/dist/dts/features/Discovery/types.js.map +1 -1
  144. package/dist/dts/features/Navigation/TopBar/AccountButton.js +1 -1
  145. package/dist/dts/features/Navigation/TopBar/AccountButton.js.map +1 -1
  146. package/dist/dts/features/Navigation/TopBar/LoginButton.js +1 -1
  147. package/dist/dts/features/Navigation/TopBar/LoginButton.js.map +1 -1
  148. package/dist/dts/features/Workspace/types.d.ts +2 -1
  149. package/dist/dts/features/Workspace/types.d.ts.map +1 -1
  150. package/dist/dts/features/Workspace/types.js.map +1 -1
  151. package/dist/dts/lib/content/types.d.ts +2 -0
  152. package/dist/dts/lib/content/types.d.ts.map +1 -1
  153. package/dist/dts/lib/session/session.d.ts.map +1 -1
  154. package/dist/dts/lib/session/session.js +6 -2
  155. package/dist/dts/lib/session/session.js.map +1 -1
  156. package/dist/dts/pages/Crosswalk/Crosswalk.d.ts.map +1 -1
  157. package/dist/dts/pages/Crosswalk/Crosswalk.js +1 -5
  158. package/dist/dts/pages/Crosswalk/Crosswalk.js.map +1 -1
  159. package/dist/dts/pages/DataDictionary/DataDictionary.d.ts.map +1 -1
  160. package/dist/dts/pages/DataDictionary/DataDictionary.js +12 -5
  161. package/dist/dts/pages/DataDictionary/DataDictionary.js.map +1 -1
  162. package/dist/dts/pages/Discovery/Discovery.d.ts.map +1 -1
  163. package/dist/dts/pages/Discovery/Discovery.js +14 -5
  164. package/dist/dts/pages/Discovery/Discovery.js.map +1 -1
  165. package/dist/dts/pages/Explorer/Explorer.d.ts +1 -1
  166. package/dist/dts/pages/Explorer/Explorer.d.ts.map +1 -1
  167. package/dist/dts/pages/Explorer/Explorer.js +13 -6
  168. package/dist/dts/pages/Explorer/Explorer.js.map +1 -1
  169. package/dist/dts/pages/Explorer/data.d.ts.map +1 -1
  170. package/dist/dts/pages/Explorer/data.js +4 -4
  171. package/dist/dts/pages/Explorer/data.js.map +1 -1
  172. package/dist/dts/pages/Landing/Landing.d.ts.map +1 -1
  173. package/dist/dts/pages/Landing/Landing.js +12 -5
  174. package/dist/dts/pages/Landing/Landing.js.map +1 -1
  175. package/dist/dts/pages/Login/Login.d.ts.map +1 -1
  176. package/dist/dts/pages/Login/Login.js +1 -5
  177. package/dist/dts/pages/Login/Login.js.map +1 -1
  178. package/dist/dts/pages/Profile/Profile.d.ts.map +1 -1
  179. package/dist/dts/pages/Profile/Profile.js +12 -5
  180. package/dist/dts/pages/Profile/Profile.js.map +1 -1
  181. package/dist/dts/pages/Query/Query.d.ts.map +1 -1
  182. package/dist/dts/pages/Query/Query.js +12 -5
  183. package/dist/dts/pages/Query/Query.js.map +1 -1
  184. package/dist/dts/pages/Query/types.d.ts +2 -1
  185. package/dist/dts/pages/Query/types.d.ts.map +1 -1
  186. package/dist/dts/pages/Workspace/Workspace.d.ts.map +1 -1
  187. package/dist/dts/pages/Workspace/Workspace.js +14 -5
  188. package/dist/dts/pages/Workspace/Workspace.js.map +1 -1
  189. package/dist/dts/types/icons.d.ts +3 -2
  190. package/dist/dts/types/icons.d.ts.map +1 -1
  191. package/dist/dts/types/icons.js +3 -2
  192. package/dist/dts/types/icons.js.map +1 -1
  193. package/dist/dts/utils/index.d.ts +2 -3
  194. package/dist/dts/utils/index.d.ts.map +1 -1
  195. package/dist/dts/utils/index.js +2 -2
  196. package/dist/dts/utils/index.js.map +1 -1
  197. package/dist/dts/utils/tests/values.test.js +0 -1
  198. package/dist/dts/utils/tests/values.test.js.map +1 -1
  199. package/dist/esm/index.js +1710 -600
  200. package/dist/esm/index.js.map +1 -1
  201. package/dist/index.d.ts +186 -172
  202. package/dist/tsconfig.tsbuildinfo +1 -1
  203. package/package.json +3 -3
package/dist/cjs/index.js CHANGED
@@ -16,6 +16,7 @@ var tinycolor = require('tinycolor2');
16
16
  var lodash = require('lodash');
17
17
  var tailwindMerge = require('tailwind-merge');
18
18
  var bs = require('react-icons/bs');
19
+ var io = require('react-icons/io');
19
20
  var Markdown = require('react-markdown');
20
21
  var remarkGfm = require('remark-gfm');
21
22
  var reactCookie = require('react-cookie');
@@ -44,7 +45,6 @@ var lu = require('react-icons/lu');
44
45
  var tw = require('tailwind-styled-components');
45
46
  require('universal-cookie');
46
47
  var ri = require('react-icons/ri');
47
- var io = require('react-icons/io');
48
48
  var modals = require('@mantine/modals');
49
49
  var GraphiQL = require('graphiql');
50
50
  var fs = require('fs');
@@ -53,6 +53,7 @@ var react$1 = require('redux-persist/integration/react');
53
53
  var reduxPersist = require('redux-persist');
54
54
  var redux = require('redux');
55
55
  var toolkit = require('@reduxjs/toolkit');
56
+ var idb = require('idb');
56
57
  var createWebStorage = require('redux-persist/lib/storage/createWebStorage');
57
58
  var form = require('@mantine/form');
58
59
  var dynamic = require('next/dynamic');
@@ -551,6 +552,47 @@ const create10ColorAccessibleContrast = (pallet)=>{
551
552
  */ const convertSecondsToMilliseconds = (seconds)=>seconds * 1000;
552
553
  const MinutesToMilliseconds = (minutes)=>minutes * 60 * 1000;
553
554
 
555
+ const convertPathsToTree = (paths)=>{
556
+ const tree = {};
557
+ paths.forEach((path)=>{
558
+ const parts = path.split('/').filter((part)=>part !== '');
559
+ let current = tree;
560
+ // Build the path through the tree
561
+ for(let i = 0; i < parts.length; i++){
562
+ const part = parts[i];
563
+ const fullPath = parts.slice(0, i + 1).join('/');
564
+ if (!current[part]) {
565
+ current[part] = {
566
+ value: fullPath,
567
+ label: part,
568
+ children: {},
569
+ _hasChildren: false
570
+ };
571
+ }
572
+ // Mark parent as having children
573
+ if (i < parts.length - 1) {
574
+ current[part]._hasChildren = true;
575
+ }
576
+ current = current[part].children;
577
+ }
578
+ });
579
+ // Convert to final format
580
+ const convertToFinalFormat = (node)=>{
581
+ const result = {
582
+ value: node.value,
583
+ label: node.label
584
+ };
585
+ const childKeys = Object.keys(node.children);
586
+ if (childKeys.length > 0) {
587
+ result.children = childKeys.map((key)=>convertToFinalFormat(node.children[key]));
588
+ }
589
+ return result;
590
+ };
591
+ // Get the root nodes and convert them
592
+ const rootKeys = Object.keys(tree);
593
+ return rootKeys.map((key)=>convertToFinalFormat(tree[key]));
594
+ };
595
+
554
596
  const resourcePathFromProjectID = (projectID)=>{
555
597
  const split = projectID.split('-');
556
598
  const program = split[0];
@@ -895,7 +937,7 @@ const UPDATE_SESSION_LIMIT = MinutesToMilliseconds(5);
895
937
  */ const SessionProvider = ({ children, updateSessionTime = 5, inactiveTimeLimit = 20, workspaceInactivityTimeLimit = 0, logoutInactiveUsers = true, monitorWorkspace = true })=>{
896
938
  const router$1 = router.useRouter();
897
939
  const coreDispatch = core.useCoreDispatch();
898
- core.useGetCSRFQuery();
940
+ const { isSuccess: isGetCSRFSuccess } = core.useGetCSRFQuery();
899
941
  useWorkspaceResourceMonitor(monitorWorkspace); // monitor workspaces if any are running or configured
900
942
  const [getUserDetails] = core.useLazyFetchUserDetailsQuery(); // Fetch user details
901
943
  const userStatus = core.useCoreSelector((state)=>core.selectUserAuthStatus(state));
@@ -982,10 +1024,14 @@ const UPDATE_SESSION_LIMIT = MinutesToMilliseconds(5);
982
1024
  updateSession,
983
1025
  endSession
984
1026
  ]);
985
- return /*#__PURE__*/ jsxRuntime.jsx(SessionContext.Provider, {
1027
+ if (isGetCSRFSuccess) return /*#__PURE__*/ jsxRuntime.jsx(SessionContext.Provider, {
986
1028
  value: value,
987
1029
  children: children
988
1030
  });
1031
+ else return /*#__PURE__*/ jsxRuntime.jsx(core$1.Center, {
1032
+ h: "100vh",
1033
+ children: /*#__PURE__*/ jsxRuntime.jsx(core$1.Loader, {})
1034
+ });
989
1035
  };
990
1036
 
991
1037
  const TooltipStyle = {
@@ -1116,6 +1162,28 @@ const ErrorCard = ({ message })=>/*#__PURE__*/ jsxRuntime.jsx(MessageCard, {
1116
1162
  color: "utility.3"
1117
1163
  });
1118
1164
 
1165
+ const EmptyTableMessage = ({ message })=>{
1166
+ return /*#__PURE__*/ jsxRuntime.jsxs(core$1.Group, {
1167
+ gap: "xs",
1168
+ justify: "center",
1169
+ classNames: {
1170
+ root: 'w-100 my-4'
1171
+ },
1172
+ children: [
1173
+ /*#__PURE__*/ jsxRuntime.jsx("span", {
1174
+ className: "text-utility-warning",
1175
+ children: /*#__PURE__*/ jsxRuntime.jsx(io.IoMdInformationCircleOutline, {
1176
+ size: "1.5rem"
1177
+ })
1178
+ }),
1179
+ /*#__PURE__*/ jsxRuntime.jsx(core$1.Text, {
1180
+ fw: 600,
1181
+ children: message
1182
+ })
1183
+ ]
1184
+ });
1185
+ };
1186
+
1119
1187
  const LoginProviderMultipleItems = ({ provider, handleLoginSelected })=>{
1120
1188
  const [value, setValue] = React.useState(null);
1121
1189
  return /*#__PURE__*/ jsxRuntime.jsxs("div", {
@@ -1550,6 +1618,7 @@ const filterRedirect = (redirect)=>{
1550
1618
  } else {
1551
1619
  redirectPath = redirect ?? '/';
1552
1620
  }
1621
+ console.log('redirectPath', redirectPath);
1553
1622
  return core.GEN3_REDIRECT_URL ? `${core.GEN3_REDIRECT_URL}/${redirectPath}` : redirectPath;
1554
1623
  };
1555
1624
  const LoginPanel = (loginConfig)=>{
@@ -2421,7 +2490,7 @@ const processChartData$2 = (facetData, valueType = 'count', total, maxBins = 100
2421
2490
  }));
2422
2491
  return results;
2423
2492
  };
2424
- const HorizontalBarChart = ({ data, valueType, total })=>{
2493
+ const HorizontalBarChart = ({ data, valueType, total, showLegendInChart = true })=>{
2425
2494
  const chartData = processChartData$2(data, valueType, total);
2426
2495
  const chartDefinition = React.useMemo(()=>{
2427
2496
  return {
@@ -2436,11 +2505,12 @@ const HorizontalBarChart = ({ data, valueType, total })=>{
2436
2505
  orient: 'vertical',
2437
2506
  left: '73%',
2438
2507
  type: 'scroll',
2439
- right: 15
2508
+ right: 15,
2509
+ show: showLegendInChart
2440
2510
  },
2441
2511
  grid: {
2442
2512
  left: '0%',
2443
- right: '30%',
2513
+ right: showLegendInChart ? '30%' : '3%',
2444
2514
  bottom: '25%',
2445
2515
  containLabel: true,
2446
2516
  height: '50%'
@@ -2565,7 +2635,8 @@ const Charts = ({ charts, data, counts, isSuccess, numCols = DEFAULT_COLS, style
2565
2635
  data: data === undefined ? [] : data,
2566
2636
  total: counts ?? 1,
2567
2637
  valueType: chart.valueType ?? 'count',
2568
- label: chart.label
2638
+ label: chart.label,
2639
+ showLegendInChart: chart.showLegendInChart
2569
2640
  })
2570
2641
  }, "modal-col-1"),
2571
2642
  /*#__PURE__*/ jsxRuntime.jsx(core$1.Grid.Col, {
@@ -2653,7 +2724,8 @@ const Charts = ({ charts, data, counts, isSuccess, numCols = DEFAULT_COLS, style
2653
2724
  data: data === undefined ? [] : data[field],
2654
2725
  total: counts ?? 1,
2655
2726
  valueType: charts[field].valueType ?? 'count',
2656
- label: charts[field].label
2727
+ label: charts[field].label,
2728
+ showLegendInChart: charts[field].showLegendInChart
2657
2729
  }),
2658
2730
  showLegends && /*#__PURE__*/ jsxRuntime.jsx(core$1.Card.Section, {
2659
2731
  inheritPadding: true,
@@ -3244,7 +3316,7 @@ const AccountButton = ({ leftIcon = undefined, rightIcon = undefined, iconSize =
3244
3316
  const mergedClassnames = mergeDefaultTailwindClassnames(classNamesDefaults, classNames);
3245
3317
  const router$1 = router.useRouter();
3246
3318
  const handleSelected = async ()=>{
3247
- await router$1.push('Profile');
3319
+ await router$1.push('/Profile');
3248
3320
  };
3249
3321
  const userInfo = core.useCoreSelector((state)=>core.selectUserDetails(state));
3250
3322
  const userStatus = core.useCoreSelector((state)=>core.selectUserAuthStatus(state));
@@ -3262,7 +3334,7 @@ const AccountButton = ({ leftIcon = undefined, rightIcon = undefined, iconSize =
3262
3334
  };
3263
3335
 
3264
3336
  const handleSelected = async (isAuthenticated, router, referrer, endSession, externalLoginUrl)=>{
3265
- if (!isAuthenticated) await router.push(externalLoginUrl || `Login?redirect=${referrer}`);
3337
+ if (!isAuthenticated) await router.push(externalLoginUrl || `/Login?redirect=${referrer}`);
3266
3338
  else {
3267
3339
  if (endSession) endSession();
3268
3340
  }
@@ -8030,6 +8102,7 @@ flex items-start justify-between items-center flex-nowrap bg-secondary px-1.5 ro
8030
8102
  if (toggleSettings) toggleSettings();
8031
8103
  },
8032
8104
  "aria-label": "Search",
8105
+ className: header.iconStyle,
8033
8106
  children: /*#__PURE__*/ jsxRuntime.jsx(react.Icon, {
8034
8107
  width: 12,
8035
8108
  height: 12,
@@ -8162,7 +8235,7 @@ const DEFAULT_MINIMUM = 0;
8162
8235
  const DEFAULT_MAXIMUM = 100;
8163
8236
  const BAD_DATA_MESSAGE = 'No data for this field';
8164
8237
 
8165
- const FacetSelector = ({ category, facetName, hooks, width = 'w-full', description, fields, selectedFields, updateSelectedField, showSearch = true, header = {
8238
+ const FacetSelector = ({ category, facetName, hooks, width = 'w-full', description, fields, selectedFields, updateSelectedField, showSearch = true, showSettings = false, header = {
8166
8239
  Panel: FacetHeader,
8167
8240
  Label: FacetText,
8168
8241
  iconStyle: controlsIconStyle
@@ -8204,6 +8277,7 @@ const FacetSelector = ({ category, facetName, hooks, width = 'w-full', descripti
8204
8277
  hooks: hooks,
8205
8278
  facetName: facetName,
8206
8279
  showSearch: showSearch,
8280
+ showSettings: showSettings,
8207
8281
  toggleSearch: toggleSearch,
8208
8282
  header: header
8209
8283
  })
@@ -9031,7 +9105,7 @@ const reducer = (state, action)=>{
9031
9105
  }
9032
9106
  }
9033
9107
  };
9034
- const QueryExpressionSection = ({ index, showImportExport = false, displayOnly = false, showTitle = false })=>{
9108
+ const QueryExpressionSection = ({ index, showImportExport = false, displayOnly = false, showTitle = true })=>{
9035
9109
  const [expandedState, setExpandedState] = React.useReducer(reducer, {});
9036
9110
  const [filtersSectionCollapsed, setFiltersSectionCollapsed] = React.useState(true);
9037
9111
  const filtersRef = React.useRef(null);
@@ -12035,7 +12109,6 @@ const TabsLayoutToComponentProp = (tabsLayout)=>{
12035
12109
  };
12036
12110
 
12037
12111
  const CohortBuilder = ({ explorerConfig, sharedFiltersMap = null, tabsLayout = 'left' })=>{
12038
- core.useGetCSRFQuery();
12039
12112
  const dispatch = core.useCoreDispatch();
12040
12113
  dispatch(core.setSharedFilters(sharedFiltersMap ?? {}));
12041
12114
  return /*#__PURE__*/ jsxRuntime.jsx("div", {
@@ -13455,15 +13528,15 @@ const FacetSelectionPanel = ({ categories, hooks, selectedFields, updateSelected
13455
13528
  hooks
13456
13529
  ]);
13457
13530
  return /*#__PURE__*/ jsxRuntime.jsx("div", {
13458
- className: "w-1/3 flex flex-col grow-0 p-4 gap-y-6",
13531
+ className: "sm:w-40 md:w-80 lg:w-96 xl:w-1/4 flex flex-col flex-shrink-0 p-4 gap-y-6",
13459
13532
  children: panels
13460
13533
  });
13461
13534
  };
13462
13535
 
13463
- const initialState$2 = {};
13536
+ const initialState$3 = {};
13464
13537
  const expandSlice = toolkit.createSlice({
13465
13538
  name: 'CohortDiscovery/expandedCategory',
13466
- initialState: initialState$2,
13539
+ initialState: initialState$3,
13467
13540
  reducers: {
13468
13541
  toggleCategoryFilter: (state, action)=>{
13469
13542
  return {
@@ -13483,10 +13556,10 @@ const filtersExpandedReducer = expandSlice.reducer;
13483
13556
  const { toggleCategoryFilter, toggleAllFilters } = expandSlice.actions;
13484
13557
  const selectFilterExpanded = (state, field)=>state.filtersExpandedState?.[field];
13485
13558
 
13486
- const initialState$1 = {};
13559
+ const initialState$2 = {};
13487
13560
  const selectIndexFacets = toolkit.createSlice({
13488
13561
  name: 'CohortDiscovery/selectedFacets',
13489
- initialState: initialState$1,
13562
+ initialState: initialState$2,
13490
13563
  reducers: {
13491
13564
  addFacetSelection: (state, action)=>{
13492
13565
  if (!(action.payload.index in state)) {
@@ -13521,12 +13594,12 @@ const selectedFacetsReducer = selectIndexFacets.reducer;
13521
13594
  const { addFacetSelection, removeFacetSelection } = selectIndexFacets.actions;
13522
13595
  const selectSelectedFacetsFromIndex = (state, index)=>state.selectedIndexFacets[index];
13523
13596
 
13524
- const initialState = {};
13597
+ const initialState$1 = {};
13525
13598
  /**
13526
13599
  * Redux slice for managing facet definitions in CohortDiscovery
13527
13600
  */ const facetDefinitionsSlice = toolkit.createSlice({
13528
13601
  name: 'CohortDiscovery/facetDefinitions',
13529
- initialState,
13602
+ initialState: initialState$1,
13530
13603
  reducers: {
13531
13604
  addFacetDefinition: (state, action)=>{
13532
13605
  const { index, definition } = action.payload;
@@ -13556,311 +13629,955 @@ const initialState = {};
13556
13629
  const facetDefinitionsReducer = facetDefinitionsSlice.reducer;
13557
13630
  const { addFacetDefinition, addFacetDefinitions } = facetDefinitionsSlice.actions;
13558
13631
 
13559
- const initialCohortState = {
13560
- cohort: {
13561
- id: 'default',
13562
- name: 'Discovery',
13563
- filters: {},
13564
- modified_datetime: new Date().toISOString()
13565
- }
13566
- };
13567
- /**
13568
- * Redux slice for cohort filters
13569
- */ const cohortSlice = toolkit.createSlice({
13570
- name: 'CohortDiscovery/cohorts',
13571
- initialState: initialCohortState,
13572
- reducers: {
13573
- // adds a filter to the cohort filter set at the given index
13574
- updateCohortFilter: (state, action)=>{
13575
- const { index, field, filter } = action.payload;
13576
- return {
13577
- cohort: {
13578
- ...state.cohort,
13579
- filters: {
13580
- ...state.cohort.filters,
13581
- [index]: {
13582
- mode: state.cohort.filters?.[index]?.mode ?? 'and',
13583
- root: {
13584
- ...state.cohort.filters?.[index]?.root ?? {},
13585
- [field]: filter
13586
- }
13587
- }
13588
- }
13589
- }
13590
- };
13591
- },
13592
- setCohortFilter: (state, action)=>{
13593
- const { index, filters } = action.payload;
13594
- return {
13595
- cohort: {
13596
- ...state.cohort,
13597
- filters: {
13598
- ...state.cohort.filters,
13599
- [index]: filters
13600
- }
13601
- }
13602
- };
13603
- },
13604
- // removes a filter to the cohort filter set at the given index
13605
- removeCohortFilter: (state, action)=>{
13606
- const { index, field } = action.payload;
13607
- const filters = state.cohort?.filters?.[index]?.root;
13608
- if (!filters) {
13609
- return;
13610
- }
13611
- const { [field]: _a, ...updated } = filters;
13612
- return {
13613
- cohort: {
13614
- ...state.cohort,
13615
- filters: {
13616
- ...state.cohort.filters,
13617
- [index]: {
13618
- mode: state.cohort.filters[index].mode,
13619
- root: updated
13620
- }
13621
- }
13622
- }
13623
- };
13624
- },
13625
- // removes all filters from the cohort filter set at the given index
13626
- clearCohortFilters: (state, action)=>{
13627
- const { index } = action.payload;
13628
- return {
13629
- cohort: {
13630
- ...state.cohort,
13631
- filters: {
13632
- ...state.cohort.filters,
13633
- [index]: {
13634
- // empty filter set
13635
- mode: 'and',
13636
- root: {}
13637
- }
13638
- }
13639
- }
13640
- };
13641
- }
13642
- }
13643
- });
13644
- // Filter actions: addFilter, removeFilter, updateFilter
13645
- const { updateCohortFilter, setCohortFilter, removeCohortFilter, clearCohortFilters } = cohortSlice.actions;
13646
- const cohortReducer = cohortSlice.reducer;
13647
-
13648
- /* eslint @typescript-eslint/no-unused-vars: 0 */ // Credit to: https://github.com/vercel/next.js/discussions/15687
13649
- const createNoopStorage = ()=>{
13650
- return {
13651
- getItem (_key) {
13652
- return Promise.resolve(null);
13653
- },
13654
- setItem (_key, value) {
13655
- return Promise.resolve(value);
13656
- },
13657
- removeItem (_key) {
13658
- return Promise.resolve();
13659
- }
13660
- };
13661
- };
13662
- const storage = typeof window !== 'undefined' ? createWebStorage('local') : createNoopStorage();
13663
- typeof window !== 'undefined' ? createWebStorage('session') : createNoopStorage();
13664
-
13665
13632
  const createCohortId = ()=>toolkit.nanoid();
13666
- const createRequestId = ()=>toolkit.nanoid();
13667
- const newCohort = (name, filters)=>{
13633
+ const newCohort = (name, filters, id, saved)=>{
13668
13634
  const ts = new Date().toISOString();
13669
13635
  return {
13670
- id: createCohortId(),
13636
+ id: id ?? createCohortId(),
13671
13637
  name: name,
13672
13638
  filters: filters,
13673
- modified_datetime: ts,
13674
- created_datetime: ts,
13639
+ modifiedDatetime: ts,
13640
+ createdDatetime: ts,
13675
13641
  requestedAccess: false,
13676
- requestId: 'no_request_id'
13642
+ requestId: 'no_request_id',
13643
+ saved: saved ?? false,
13644
+ modified: false
13677
13645
  };
13678
13646
  };
13679
- const newDataAccessRequest = (userInformation, cohortId)=>{
13647
+ const newDataAccessRequest = (requestId, status, userInformation, cohortId, createdTime, updatedTime)=>{
13648
+ const now = new Date().toISOString();
13680
13649
  return {
13681
- id: createRequestId(),
13682
- request_datetime: new Date().toISOString(),
13683
- status: 'pending',
13650
+ id: requestId,
13651
+ createdDatetime: createdTime ?? now,
13652
+ updatedDatetime: updatedTime ?? now,
13653
+ status: status,
13684
13654
  cohortId,
13685
13655
  ...userInformation
13686
13656
  };
13687
13657
  };
13658
+ const isIndexedFilterSetEmpty = (filters)=>Object.values(filters).every((filterSet)=>Object.keys(filterSet).length === 0);
13688
13659
 
13689
- // Create the entity adapter
13690
- const cohortsAdapter = toolkit.createEntityAdapter({
13691
- // Select the id as the primary key
13692
- selectId: (cohort)=>cohort.id,
13693
- // Optional: Sort by name if needed
13694
- sortComparer: (a, b)=>a.name.localeCompare(b.name)
13695
- });
13696
- // Create a slice with reducers
13697
- const cohortPersistenceSlice = toolkit.createSlice({
13698
- name: 'CohortDiscovery/CohortManager',
13699
- initialState: cohortsAdapter.getInitialState(),
13700
- reducers: {
13701
- // Add a single cohort
13702
- saveCohort: (state, action)=>{
13703
- const cohort = newCohort(action.payload.name, action.payload.filters);
13704
- cohortsAdapter.setOne(state, cohort);
13705
- },
13706
- // Remove a cohort
13707
- removeCohort: cohortsAdapter.removeOne,
13708
- // Remove multiple cohorts
13709
- removeCohorts: cohortsAdapter.removeMany
13660
+ const DATABASE_NAME = 'Gen3CohortDiscovery';
13661
+ const STORE_NAME = 'cohorts';
13662
+ const DB_SCHEMA_VERSION = 1;
13663
+ class CohortStorage {
13664
+ getDb() {
13665
+ try {
13666
+ return idb.openDB(DATABASE_NAME, 1, {
13667
+ upgrade (db) {
13668
+ if (!db.objectStoreNames.contains(STORE_NAME)) {
13669
+ db.createObjectStore(STORE_NAME, {
13670
+ keyPath: 'id'
13671
+ });
13672
+ }
13673
+ }
13674
+ });
13675
+ } catch (error) {
13676
+ let errorMessage = 'Unknown error';
13677
+ if (error instanceof Error) {
13678
+ errorMessage = error.message;
13679
+ }
13680
+ throw new Error(`Database initialization failed: ${errorMessage}`);
13681
+ }
13710
13682
  }
13711
- });
13712
- // Export actions
13713
- const { saveCohort, removeCohort, removeCohorts } = cohortPersistenceSlice.actions;
13714
- // Export selectors
13715
- const { selectAll: selectAllCohorts, selectTotal: selectTotalCohorts, selectById: selectCohortById } = cohortsAdapter.getSelectors((state)=>state.savedCohorts);
13716
- const selectCohortIdToNameMap = toolkit.createSelector([
13717
- selectAllCohorts
13718
- ], (cohorts)=>{
13719
- return cohorts.reduce((acc, cohort)=>{
13720
- acc[cohort.id] = cohort.name;
13721
- return acc;
13722
- }, {});
13723
- });
13724
- // Export reducer
13725
- const cohortPersistenceReducer = cohortPersistenceSlice.reducer;
13726
-
13727
- // Create the entity adapter
13728
- const dataAccessRequestsAdapter = toolkit.createEntityAdapter({
13729
- // Select the id as the primary key
13730
- selectId: (request)=>request.id,
13731
- // Optional: Sort by request_datetime descending (newest first)
13732
- sortComparer: (a, b)=>new Date(b.request_datetime).getTime() - new Date(a.request_datetime).getTime()
13733
- });
13734
- // Create a slice with reducers
13735
- const dataAccessRequestsSlice = toolkit.createSlice({
13736
- name: 'dataAccessRequests',
13737
- initialState: dataAccessRequestsAdapter.getInitialState(),
13738
- reducers: {
13739
- addDataAccessRequest: (state, action)=>{
13740
- const { cohortId, userAccessInformation } = action.payload;
13741
- const dataAccessRequests = newDataAccessRequest(userAccessInformation, cohortId);
13742
- dataAccessRequestsAdapter.addOne(state, dataAccessRequests);
13743
- },
13744
- // Set a request's status
13745
- setRequestStatus: (state, action)=>{
13746
- const { id, status } = action.payload;
13747
- dataAccessRequestsAdapter.updateOne(state, {
13748
- id,
13749
- changes: {
13750
- status
13683
+ /**
13684
+ * Initialize the IndexedDB database with proper schema
13685
+ */ async initializeDB() {
13686
+ try {
13687
+ return await idb.openDB(DATABASE_NAME, DB_SCHEMA_VERSION, {
13688
+ upgrade: (db)=>{
13689
+ if (!db.objectStoreNames.contains(STORE_NAME)) {
13690
+ db.createObjectStore(STORE_NAME, {
13691
+ keyPath: 'id'
13692
+ });
13693
+ }
13694
+ },
13695
+ blocked: ()=>{
13696
+ console.warn('CohortDB: Database upgrade blocked by another connection');
13697
+ },
13698
+ blocking: ()=>{
13699
+ console.warn('CohortDB: This connection is blocking a database upgrade');
13700
+ },
13701
+ terminated: ()=>{
13702
+ console.error('CohortDB: Database connection terminated unexpectedly');
13751
13703
  }
13752
13704
  });
13705
+ } catch (error) {
13706
+ let errorMessage = 'Unknown error';
13707
+ if (error instanceof Error) {
13708
+ errorMessage = error.message;
13709
+ }
13710
+ throw new Error(`Database initialization failed: ${errorMessage}`);
13753
13711
  }
13754
13712
  }
13755
- });
13756
- // Export actions
13757
- const { addDataAccessRequest, setRequestStatus } = dataAccessRequestsSlice.actions;
13758
- // Export selectors
13759
- const { selectAll: selectAllDataAccessRequests, selectById: selectDataAccessRequestById, selectIds: selectDataAccessRequestIds, selectEntities: selectDataAccessRequestEntities, selectTotal: selectTotalDataAccessRequests } = dataAccessRequestsAdapter.getSelectors((state)=>state.dataAccessRequests);
13760
- const selectCohortToRequestId = toolkit.createSelector([
13761
- selectAllDataAccessRequests
13762
- ], (requests)=>{
13763
- return requests.reduce((acc, request)=>{
13764
- acc[request.cohortId] = request.id;
13765
- return acc;
13766
- }, {});
13767
- });
13768
- // Export reducer
13769
- const dataAccessRequestsReducer = dataAccessRequestsSlice.reducer;
13770
-
13771
- const _APP_NAME$3 = 'CohortDiscovery';
13772
- const persistConfig = {
13773
- key: _APP_NAME$3,
13774
- version: 1,
13775
- storage
13776
- };
13777
- // create the store, context and selector for the ProjectsCenter
13778
- // Note the project app has a local store and context which isolates
13779
- // the filters and other store/cache values
13780
- const reducers = redux.combineReducers({
13781
- selectedIndexFacets: selectedFacetsReducer,
13782
- filtersExpandedState: filtersExpandedReducer,
13783
- facetDefinitionState: facetDefinitionsReducer,
13784
- cohorts: cohortReducer,
13785
- savedCohorts: cohortPersistenceReducer,
13786
- dataAccessRequests: dataAccessRequestsReducer
13787
- });
13788
- const persistedReducers = reduxPersist.persistReducer(persistConfig, reducers);
13789
- const { id, AppStore, AppContext, useAppSelector, useAppDispatch } = core.createAppStore({
13790
- reducers: persistedReducers,
13791
- name: _APP_NAME$3,
13792
- version: '0.0.1'
13793
- });
13794
-
13795
- const selectCohortFilters = (state)=>state.cohorts.cohort.filters;
13796
- const selectCurrentCohortId = (state)=>state.cohorts.cohort.id;
13797
- const selectCurrentCohortName = (state)=>state.cohorts.cohort.name;
13798
- /**
13799
- * Select a filter by its name from the current cohort. If the filter is not found
13800
- * returns undefined.
13801
- * @param state - Core
13802
- * @param index which cohort index to select from
13803
- * @param name name of the filter to select
13804
- */ const selectIndexedFilterByName = (state, index, name)=>{
13805
- return state.cohorts.cohort.filters[index]?.root[name];
13806
- };
13807
- const EmptyFilterSet = {
13808
- mode: 'and',
13809
- root: {}
13810
- };
13811
- /**
13812
- * Select a filter from the index.
13813
- * returns undefined.
13814
- * @param state - Core
13815
- * @param index which cohort index to select from
13816
- */ const selectIndexFilters = (state, index)=>{
13817
- return state.cohorts.cohort.filters?.[index] ?? EmptyFilterSet; // TODO: check if this is undefined
13818
- };
13819
-
13820
- const useToggleExpandFilter = ()=>{
13821
- const dispatch = useAppDispatch();
13822
- return (field, expanded)=>{
13823
- dispatch(toggleCategoryFilter({
13824
- field,
13825
- expanded
13826
- }));
13827
- };
13828
- };
13829
- const useFilterExpandedState = (field)=>{
13830
- return useAppSelector((state)=>selectFilterExpanded(state, field));
13831
- };
13832
- const useCohortFacetFilters = (index)=>{
13833
- return useAppSelector((state)=>selectCohortFilters(state)[index]);
13834
- };
13835
- const useClearFilters = (index)=>{
13836
- const dispatch = useAppDispatch();
13837
- return (field)=>{
13838
- dispatch(removeCohortFilter({
13839
- index,
13840
- field
13841
- }));
13842
- };
13843
- };
13844
- /**
13845
- * Update Guppy filters: process nested fields and have the final
13846
- * leaf be filtered
13847
- * @param index
13848
- */ const useUpdateFilters = (index)=>{
13849
- const dispatch = useAppDispatch();
13850
- // update the filter for this facet
13851
- return (field, filter)=>{
13852
- dispatch(updateCohortFilter({
13853
- index: index,
13854
- field: field,
13855
- filter: buildNested(field, filter)
13713
+ // ===== CREATE OPERATIONS =====
13714
+ /**
13715
+ * Save a single cohort to the database
13716
+ */ async saveCohort(cohort) {
13717
+ try {
13718
+ const db = await this.getDb();
13719
+ const tx = db.transaction(STORE_NAME, 'readwrite');
13720
+ tx.objectStore(STORE_NAME).put(cohort);
13721
+ await tx.done;
13722
+ return {
13723
+ status: 200,
13724
+ message: 'cohort added'
13725
+ };
13726
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
13727
+ } catch (_error) {
13728
+ return {
13729
+ isError: true,
13730
+ status: 500,
13731
+ message: 'unable to save cohort'
13732
+ };
13733
+ }
13734
+ }
13735
+ /**
13736
+ * Save multiple cohorts in a single transaction (bulk operation)
13737
+ */ async saveCohorts(cohorts) {
13738
+ if (cohorts.length === 0) return {
13739
+ isError: true,
13740
+ status: 400,
13741
+ message: 'cannot add an empty array'
13742
+ };
13743
+ try {
13744
+ const db = await this.getDb();
13745
+ const tx = db.transaction(STORE_NAME, 'readwrite');
13746
+ // Batch all operations in single transaction for better performance
13747
+ await Promise.all([
13748
+ ...cohorts.map((cohort)=>tx.store.put({
13749
+ ...cohort,
13750
+ saved: true
13751
+ })),
13752
+ tx.done
13753
+ ]);
13754
+ return {
13755
+ status: 200,
13756
+ message: 'cohorts added'
13757
+ };
13758
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
13759
+ } catch (_error) {
13760
+ return {
13761
+ isError: true,
13762
+ status: 500,
13763
+ message: 'unable to save cohort'
13764
+ };
13765
+ }
13766
+ }
13767
+ // ===== READ OPERATIONS =====
13768
+ /**
13769
+ * Get a specific cohort by ID
13770
+ */ async getCohort(id) {
13771
+ try {
13772
+ const db = await this.getDb();
13773
+ const tx = db.transaction(STORE_NAME, 'readonly');
13774
+ const store = tx.objectStore(STORE_NAME);
13775
+ const cohort = await store.get(id);
13776
+ return {
13777
+ status: 200,
13778
+ message: 'success',
13779
+ data: cohort
13780
+ };
13781
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
13782
+ } catch (_error) {
13783
+ return {
13784
+ isError: true,
13785
+ status: 401,
13786
+ message: `cannot find cohort ${id}`
13787
+ };
13788
+ }
13789
+ }
13790
+ /**
13791
+ * Get all cohorts from the database
13792
+ */ async getAllCohorts() {
13793
+ try {
13794
+ const db = await this.getDb();
13795
+ const tx = db.transaction(STORE_NAME, 'readonly');
13796
+ const store = tx.objectStore(STORE_NAME);
13797
+ const savedCohorts = await store.getAll();
13798
+ if (!savedCohorts) {
13799
+ return {
13800
+ isError: true,
13801
+ status: 500,
13802
+ message: 'no cohorts returned'
13803
+ };
13804
+ }
13805
+ const cohorts = savedCohorts.reduce((acc, cohort)=>{
13806
+ const { id } = cohort;
13807
+ acc[id] = cohort;
13808
+ return acc;
13809
+ }, {});
13810
+ return {
13811
+ status: 200,
13812
+ message: 'success',
13813
+ data: cohorts
13814
+ };
13815
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
13816
+ } catch (_error) {
13817
+ return {
13818
+ isError: true,
13819
+ status: 401,
13820
+ message: 'cannot return cohorts'
13821
+ };
13822
+ }
13823
+ }
13824
+ /**
13825
+ * Search cohorts by name (case-insensitive partial match)
13826
+ */ async searchCohortsByName(searchTerm) {
13827
+ try {
13828
+ const db = await this.getDb();
13829
+ const tx = db.transaction(STORE_NAME, 'readonly');
13830
+ const store = tx.objectStore(STORE_NAME);
13831
+ const allCohorts = await store.getAll(STORE_NAME);
13832
+ // Filter in memory for partial name matching
13833
+ const searchLower = searchTerm.toLowerCase();
13834
+ return {
13835
+ status: 200,
13836
+ message: 'success',
13837
+ data: allCohorts.filter((cohort)=>cohort.name.toLowerCase().includes(searchLower)).reduce((acc, cohort)=>{
13838
+ const { id } = cohort;
13839
+ acc[id] = cohort;
13840
+ return acc;
13841
+ }, {})
13842
+ };
13843
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
13844
+ } catch (_error) {
13845
+ return {
13846
+ isError: true,
13847
+ status: 401,
13848
+ message: 'cannot find cohorts'
13849
+ };
13850
+ }
13851
+ }
13852
+ /**
13853
+ * Count total number of cohorts
13854
+ */ async getCohortCount() {
13855
+ try {
13856
+ const db = await this.getDb();
13857
+ const total = await db.count(STORE_NAME);
13858
+ return {
13859
+ status: 200,
13860
+ message: 'success',
13861
+ data: total
13862
+ };
13863
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
13864
+ } catch (_error) {
13865
+ return {
13866
+ isError: true,
13867
+ status: 401,
13868
+ message: 'cannot find cohort count'
13869
+ };
13870
+ }
13871
+ }
13872
+ // ===== UPDATE OPERATIONS =====
13873
+ /**
13874
+ * Update an existing cohort (full replacement)
13875
+ */ async updateCohort(cohort) {
13876
+ try {
13877
+ const db = await this.getDb();
13878
+ // Verify cohort exists before updating
13879
+ const tx = db.transaction(STORE_NAME, 'readwrite');
13880
+ const store = tx.objectStore(STORE_NAME);
13881
+ const existing = await store.get(cohort.id);
13882
+ if (!existing) {
13883
+ return {
13884
+ isError: true,
13885
+ status: 401,
13886
+ message: 'cohort not found'
13887
+ };
13888
+ }
13889
+ const timestamp = core.getTimestamp();
13890
+ const updated = {
13891
+ ...existing,
13892
+ modifiedDatetime: timestamp
13893
+ };
13894
+ store.put(updated);
13895
+ await tx.done;
13896
+ return {
13897
+ status: 200,
13898
+ message: 'success'
13899
+ };
13900
+ } catch (error) {
13901
+ let errorMessage = 'An unknown error occurred';
13902
+ if (error instanceof Error) {
13903
+ errorMessage = error.message;
13904
+ }
13905
+ return {
13906
+ isError: true,
13907
+ status: 500,
13908
+ message: `Unable to update cohort: ${cohort.id}. Error: ${errorMessage}`
13909
+ };
13910
+ }
13911
+ }
13912
+ // ===== DELETE OPERATIONS =====
13913
+ /**
13914
+ * Delete a specific cohort by ID
13915
+ */ async deleteCohort(id) {
13916
+ try {
13917
+ const db = await this.getDb();
13918
+ const tx = db.transaction(STORE_NAME, 'readwrite');
13919
+ const store = tx.objectStore(STORE_NAME);
13920
+ // Verify cohort exists before deleting
13921
+ const existing = await db.get(STORE_NAME, id);
13922
+ if (!existing) {
13923
+ return {
13924
+ isError: true,
13925
+ status: 401,
13926
+ message: 'cohort not found'
13927
+ };
13928
+ }
13929
+ store.delete(id);
13930
+ await tx.done;
13931
+ return {
13932
+ status: 200,
13933
+ message: `${id} deleted`
13934
+ };
13935
+ } catch (error) {
13936
+ const errorMessage = error instanceof Error ? error.message : 'An unknown error occurred';
13937
+ return {
13938
+ isError: true,
13939
+ status: 500,
13940
+ message: `Unable to delete cohort: ${id}. Error: ${errorMessage}`
13941
+ };
13942
+ }
13943
+ }
13944
+ /**
13945
+ * Delete all cohorts from the database
13946
+ */ async deleteAllCohorts() {
13947
+ try {
13948
+ const db = await this.getDb();
13949
+ const tx = db.transaction(STORE_NAME, 'readwrite');
13950
+ const store = tx.objectStore(STORE_NAME);
13951
+ store.clear();
13952
+ await tx.done;
13953
+ return {
13954
+ status: 200,
13955
+ message: `all cohorts deleted`
13956
+ };
13957
+ } catch (error) {
13958
+ const errorMessage = error instanceof Error ? error.message : 'An unknown error occurred';
13959
+ return {
13960
+ isError: true,
13961
+ status: 500,
13962
+ message: `Unable to delete all cohorts. Error: ${errorMessage}`
13963
+ };
13964
+ }
13965
+ }
13966
+ // ===== UTILITY OPERATIONS =====
13967
+ /**
13968
+ * Check if a cohort exists
13969
+ */ async cohortExists(id) {
13970
+ try {
13971
+ const db = await this.getDb();
13972
+ const tx = db.transaction(STORE_NAME, 'readwrite');
13973
+ const store = tx.objectStore(STORE_NAME);
13974
+ // Verify cohort exists before deleting
13975
+ const existing = await db.get(STORE_NAME, id);
13976
+ return {
13977
+ status: 200,
13978
+ message: `${id}: ${existing ? 'true' : 'false'}`
13979
+ };
13980
+ } catch (error) {
13981
+ const errorMessage = error instanceof Error ? error.message : 'An unknown error occurred';
13982
+ return {
13983
+ isError: true,
13984
+ status: 500,
13985
+ message: `Unable search for cohort. Error: ${errorMessage}`
13986
+ };
13987
+ }
13988
+ }
13989
+ /**
13990
+ * Export all cohorts as JSON
13991
+ */ async exportCohorts() {
13992
+ return await this.getAllCohorts();
13993
+ }
13994
+ /**
13995
+ * Import cohorts from JSON data
13996
+ */ async importCohorts(cohorts, overwrite = false) {
13997
+ try {
13998
+ if (overwrite) {
13999
+ await this.deleteAllCohorts();
14000
+ }
14001
+ await this.saveCohorts(cohorts);
14002
+ } catch (error) {
14003
+ const errorMessage = error instanceof Error ? error.message : 'An unknown error occurred';
14004
+ return {
14005
+ isError: true,
14006
+ status: 500,
14007
+ message: `Failed to import cohorts: ${errorMessage}`
14008
+ };
14009
+ }
14010
+ }
14011
+ /**
14012
+ * Close the database connection
14013
+ */ async close() {
14014
+ try {
14015
+ const db = await this.getDb();
14016
+ db.close();
14017
+ } catch (error) {
14018
+ console.error('Failed to close database:', error);
14019
+ }
14020
+ }
14021
+ }
14022
+ // Export a singleton instance
14023
+ const cohortStorage = new CohortStorage();
14024
+
14025
+ const EmptyFilterSet = {
14026
+ mode: 'and',
14027
+ root: {}
14028
+ };
14029
+
14030
+ const isNameUnique = (state, name, excludeId)=>{
14031
+ const trimmedName = name.trim();
14032
+ if (!trimmedName) return false;
14033
+ return !Object.values(state.entities).some((cohort)=>cohort && cohort.id !== excludeId && cohort.name.trim().toLowerCase() === trimmedName.toLowerCase());
14034
+ };
14035
+ const generateUniqueName = (state, baseName)=>{
14036
+ const trimmedBaseName = baseName.trim();
14037
+ // If base name is unique, use it
14038
+ if (isNameUnique(state, trimmedBaseName)) {
14039
+ return trimmedBaseName;
14040
+ }
14041
+ // Find a unique name by appending numbers
14042
+ let counter = 1;
14043
+ let uniqueName;
14044
+ do {
14045
+ uniqueName = `${trimmedBaseName} (${counter})`;
14046
+ counter++;
14047
+ }while (!isNameUnique(state, uniqueName))
14048
+ return uniqueName;
14049
+ };
14050
+ const createDefaultCohort = ()=>newCohort(DEFAULT_COHORT_NAME, {}, undefined, false);
14051
+ // Entity adapter
14052
+ const cohortsAdapter = toolkit.createEntityAdapter({
14053
+ selectId: (cohort)=>cohort.id,
14054
+ sortComparer: (a, b)=>{
14055
+ if (a.modifiedDatetime > b.modifiedDatetime) return -1;
14056
+ if (a.modifiedDatetime < b.modifiedDatetime) return 1;
14057
+ return 0;
14058
+ }
14059
+ });
14060
+ //const DEFAULT_COHORT_ID = 'default';
14061
+ const DEFAULT_COHORT_NAME = 'Default';
14062
+ const loadCohortsFromStorage = toolkit.createAsyncThunk('cohorts/loadFromStorage', async ()=>{
14063
+ try {
14064
+ const cohortsMap = await cohortStorage.getAllCohorts();
14065
+ return Object(cohortsMap).values();
14066
+ } catch (error) {
14067
+ console.error('Failed to load cohorts:', error);
14068
+ return [];
14069
+ }
14070
+ });
14071
+ const saveCohortToStorage = toolkit.createAsyncThunk('cohorts/saveToStorage', async (params, { getState })=>{
14072
+ const { cohortId, cohortName } = params;
14073
+ const state = getState();
14074
+ const cohort = state.cohorts.entities[cohortId];
14075
+ if (!cohort) {
14076
+ throw new Error(`Cohort with id ${cohortId} not found`);
14077
+ }
14078
+ const cohortToSave = {
14079
+ ...cohort,
14080
+ name: cohortName || cohort.name,
14081
+ saved: true,
14082
+ modified: false,
14083
+ modifiedDatetime: new Date().toISOString()
14084
+ };
14085
+ await cohortStorage.saveCohort(cohortToSave);
14086
+ return cohortToSave;
14087
+ });
14088
+ const autoSaveCohort = toolkit.createAsyncThunk('cohorts/autoSave', async (cohortId, { getState })=>{
14089
+ const state = getState();
14090
+ const cohort = state.cohorts.entities[cohortId];
14091
+ if (!cohort || !cohort.saved) {
14092
+ throw new Error(`Cannot auto-save unsaved cohort ${cohortId}`);
14093
+ }
14094
+ const cohortToSave = {
14095
+ ...cohort,
14096
+ modified: false,
14097
+ modifiedDatetime: new Date().toISOString()
14098
+ };
14099
+ await cohortStorage.saveCohort(cohortToSave);
14100
+ return cohortToSave;
14101
+ });
14102
+ const deleteCohortFromStorage = toolkit.createAsyncThunk('cohorts/deleteFromStorage', async (cohortId)=>{
14103
+ await cohortStorage.deleteCohort(cohortId);
14104
+ return cohortId;
14105
+ });
14106
+ const emptyInitialState = cohortsAdapter.getInitialState({
14107
+ currentCohortId: 'empty',
14108
+ loading: false,
14109
+ error: null,
14110
+ autoSaveInProgress: []
14111
+ });
14112
+ const initialState = cohortsAdapter.addOne(emptyInitialState, createDefaultCohort());
14113
+ const updateAutoSave = (cohortId, state)=>{
14114
+ // Add to auto-save queue if not already present
14115
+ if (!state.autoSaveInProgress.includes(cohortId)) {
14116
+ state.autoSaveInProgress.push(cohortId);
14117
+ }
14118
+ };
14119
+ const cohortManagerSlice = toolkit.createSlice({
14120
+ name: 'CohortDiscovery/CohortManager',
14121
+ initialState,
14122
+ reducers: {
14123
+ setCurrentCohortId: (state, action)=>{
14124
+ if (state.entities[action.payload]) {
14125
+ state.currentCohortId = action.payload;
14126
+ }
14127
+ },
14128
+ createNewCohort: (state, action)=>{
14129
+ const baseName = action.payload.name || `New Cohort`;
14130
+ const uniqueName = generateUniqueName(state, baseName);
14131
+ const cohort = newCohort(uniqueName, action.payload.filters || {}, undefined, false);
14132
+ cohortsAdapter.addOne(state, cohort);
14133
+ state.currentCohortId = cohort.id;
14134
+ },
14135
+ removeCohort: (state, action)=>{
14136
+ const cohortId = action.payload;
14137
+ const totalCohorts = Object.keys(state.entities).length;
14138
+ if (totalCohorts <= 1) {
14139
+ cohortsAdapter.removeAll(state);
14140
+ const defaultCohort = createDefaultCohort();
14141
+ cohortsAdapter.addOne(state, defaultCohort);
14142
+ state.currentCohortId = defaultCohort.id;
14143
+ return;
14144
+ }
14145
+ cohortsAdapter.removeOne(state, cohortId);
14146
+ if (state.currentCohortId === cohortId) {
14147
+ const remainingIds = Object.keys(state.entities);
14148
+ state.currentCohortId = remainingIds[0];
14149
+ }
14150
+ state.autoSaveInProgress = state.autoSaveInProgress.filter((id)=>id !== cohortId);
14151
+ },
14152
+ updateCohortName: (state, action)=>{
14153
+ const { id, name } = action.payload;
14154
+ const cohort = state.entities[id];
14155
+ if (!cohort) return;
14156
+ const trimmedName = name.trim();
14157
+ // Don't update if name is empty
14158
+ if (!trimmedName) return;
14159
+ // Don't update if name hasn't changed
14160
+ if (cohort.name === trimmedName) return;
14161
+ // Check if name is unique (excluding current cohort)
14162
+ if (!isNameUnique(state, trimmedName, id)) {
14163
+ // Set error for duplicate name
14164
+ state.error = `A cohort named "${trimmedName}" already exists. Please choose a different name.`;
14165
+ return;
14166
+ }
14167
+ const wasSaved = cohort.saved;
14168
+ cohortsAdapter.updateOne(state, {
14169
+ id,
14170
+ changes: {
14171
+ name: trimmedName,
14172
+ modified: !wasSaved,
14173
+ modifiedDatetime: new Date().toISOString()
14174
+ }
14175
+ });
14176
+ if (wasSaved) {
14177
+ // Add to auto-save queue if not already present
14178
+ updateAutoSave(id, state);
14179
+ }
14180
+ },
14181
+ updateCohortFilter: (state, action)=>{
14182
+ const { index, field, filter } = action.payload;
14183
+ const currentCohort = getCurrentCohort$1(state);
14184
+ const wasSaved = currentCohort.saved;
14185
+ cohortsAdapter.updateOne(state, {
14186
+ id: currentCohort.id,
14187
+ changes: {
14188
+ filters: {
14189
+ ...currentCohort.filters,
14190
+ [index]: {
14191
+ mode: currentCohort.filters?.[index]?.mode ?? 'and',
14192
+ root: {
14193
+ ...currentCohort.filters?.[index]?.root ?? {},
14194
+ [field]: filter
14195
+ }
14196
+ }
14197
+ },
14198
+ modified: !wasSaved,
14199
+ modifiedDatetime: new Date().toISOString()
14200
+ }
14201
+ });
14202
+ if (wasSaved) {
14203
+ // Add to auto-save queue if not already present
14204
+ updateAutoSave(currentCohort.id, state);
14205
+ }
14206
+ },
14207
+ setCohortFilter: (state, action)=>{
14208
+ const { index, filters } = action.payload;
14209
+ const currentCohort = getCurrentCohort$1(state);
14210
+ const wasSaved = currentCohort.saved;
14211
+ cohortsAdapter.updateOne(state, {
14212
+ id: currentCohort.id,
14213
+ changes: {
14214
+ filters: {
14215
+ ...currentCohort.filters,
14216
+ [index]: filters
14217
+ },
14218
+ modified: !wasSaved,
14219
+ modifiedDatetime: new Date().toISOString()
14220
+ }
14221
+ });
14222
+ if (wasSaved) {
14223
+ updateAutoSave(currentCohort.id, state);
14224
+ }
14225
+ },
14226
+ setAllIndexFilters: (state, action)=>{
14227
+ const currentCohort = getCurrentCohort$1(state);
14228
+ const wasSaved = currentCohort.saved;
14229
+ cohortsAdapter.updateOne(state, {
14230
+ id: currentCohort.id,
14231
+ changes: {
14232
+ filters: action.payload,
14233
+ modified: !wasSaved,
14234
+ modifiedDatetime: new Date().toISOString()
14235
+ }
14236
+ });
14237
+ if (wasSaved) {
14238
+ updateAutoSave(currentCohort.id, state);
14239
+ }
14240
+ },
14241
+ removeCohortFilter: (state, action)=>{
14242
+ const { index, field } = action.payload;
14243
+ const currentCohort = getCurrentCohort$1(state);
14244
+ const filters = currentCohort.filters?.[index]?.root;
14245
+ const wasSaved = currentCohort.saved;
14246
+ if (!filters) return;
14247
+ const { [field]: _removed, ...updated } = filters;
14248
+ cohortsAdapter.updateOne(state, {
14249
+ id: currentCohort.id,
14250
+ changes: {
14251
+ filters: {
14252
+ ...currentCohort.filters,
14253
+ [index]: {
14254
+ mode: currentCohort.filters[index].mode,
14255
+ root: updated
14256
+ }
14257
+ },
14258
+ modified: !wasSaved,
14259
+ modifiedDatetime: new Date().toISOString()
14260
+ }
14261
+ });
14262
+ if (wasSaved) {
14263
+ updateAutoSave(currentCohort.id, state);
14264
+ }
14265
+ },
14266
+ clearCohortFilters: (state, action)=>{
14267
+ const { index } = action.payload;
14268
+ const currentCohort = getCurrentCohort$1(state);
14269
+ const wasSaved = currentCohort.saved;
14270
+ cohortsAdapter.updateOne(state, {
14271
+ id: currentCohort.id,
14272
+ changes: {
14273
+ filters: {
14274
+ ...currentCohort.filters,
14275
+ [index]: EmptyFilterSet
14276
+ },
14277
+ modified: !wasSaved,
14278
+ modifiedDatetime: new Date().toISOString()
14279
+ }
14280
+ });
14281
+ if (wasSaved) {
14282
+ updateAutoSave(currentCohort.id, state);
14283
+ }
14284
+ },
14285
+ clearError: (state)=>{
14286
+ state.error = null;
14287
+ }
14288
+ },
14289
+ extraReducers: (builder)=>{
14290
+ builder.addCase(loadCohortsFromStorage.pending, (state)=>{
14291
+ state.loading = true;
14292
+ state.error = null;
14293
+ }).addCase(loadCohortsFromStorage.fulfilled, (state, action)=>{
14294
+ state.loading = false;
14295
+ if (action.payload.length > 0) {
14296
+ if (Object.keys(state.entities).length === 1) {
14297
+ cohortsAdapter.removeOne(state, state.entities[0].id);
14298
+ }
14299
+ cohortsAdapter.addMany(state, action.payload);
14300
+ const sortedCohorts = action.payload.sort((a, b)=>b.modifiedDatetime.localeCompare(a.modifiedDatetime));
14301
+ state.currentCohortId = sortedCohorts[0].id;
14302
+ }
14303
+ }).addCase(loadCohortsFromStorage.rejected, (state, action)=>{
14304
+ state.loading = false;
14305
+ state.error = action.error.message || 'Failed to load cohorts';
14306
+ }).addCase(saveCohortToStorage.fulfilled, (state, action)=>{
14307
+ cohortsAdapter.updateOne(state, {
14308
+ id: action.payload.id,
14309
+ changes: action.payload
14310
+ });
14311
+ }).addCase(saveCohortToStorage.rejected, (state, action)=>{
14312
+ state.error = action.error.message || 'Failed to save cohort';
14313
+ }).addCase(autoSaveCohort.pending, (state, action)=>{
14314
+ updateAutoSave(action.meta.arg, state);
14315
+ }).addCase(autoSaveCohort.fulfilled, (state, action)=>{
14316
+ state.autoSaveInProgress = state.autoSaveInProgress.filter((id)=>id !== action.payload.id);
14317
+ cohortsAdapter.updateOne(state, {
14318
+ id: action.payload.id,
14319
+ changes: action.payload
14320
+ });
14321
+ }).addCase(autoSaveCohort.rejected, (state, action)=>{
14322
+ state.autoSaveInProgress = state.autoSaveInProgress.filter((id)=>id !== action.meta.arg);
14323
+ console.warn(`Auto-save failed for cohort ${action.meta.arg}:`, action.error.message);
14324
+ }).addCase(deleteCohortFromStorage.fulfilled, (state, action)=>{
14325
+ const cohortId = action.payload;
14326
+ const totalCohorts = Object.keys(state.entities).length;
14327
+ if (totalCohorts <= 1) {
14328
+ cohortsAdapter.removeAll(state);
14329
+ const defaultCohort = createDefaultCohort();
14330
+ cohortsAdapter.addOne(state, defaultCohort);
14331
+ state.currentCohortId = defaultCohort.id;
14332
+ } else {
14333
+ cohortsAdapter.removeOne(state, cohortId);
14334
+ if (state.currentCohortId === cohortId) {
14335
+ const remainingIds = Object.keys(state.entities);
14336
+ state.currentCohortId = remainingIds[0];
14337
+ }
14338
+ }
14339
+ state.autoSaveInProgress = state.autoSaveInProgress.filter((id)=>id !== cohortId);
14340
+ });
14341
+ }
14342
+ });
14343
+ // Helper function
14344
+ const getCurrentCohort$1 = (state)=>{
14345
+ const cohort = state.entities[state.currentCohortId];
14346
+ if (!cohort) {
14347
+ return createDefaultCohort();
14348
+ }
14349
+ return cohort;
14350
+ };
14351
+ // Actions export
14352
+ const { setCurrentCohortId, createNewCohort, removeCohort, updateCohortName, updateCohortFilter, setCohortFilter, setAllIndexFilters, removeCohortFilter, clearCohortFilters } = cohortManagerSlice.actions;
14353
+ const cohortManagerReducer = cohortManagerSlice.reducer;
14354
+
14355
+ const getCurrentCohort = (state)=>{
14356
+ const cohort = state.entities[state.currentCohortId];
14357
+ if (!cohort) {
14358
+ return createDefaultCohort();
14359
+ }
14360
+ return cohort;
14361
+ };
14362
+ const autoSaveMiddleware = (store)=>(next)=>(action)=>{
14363
+ const result = next(action);
14364
+ const autoSaveActions = [
14365
+ 'updateCohortName',
14366
+ 'updateCohortFilter',
14367
+ 'setCohortFilter',
14368
+ 'setAllIndexFilters',
14369
+ 'removeCohortFilter',
14370
+ 'clearCohortFilters'
14371
+ ];
14372
+ if (autoSaveActions.some((actionType)=>action.type.includes(actionType))) {
14373
+ const state = store.getState();
14374
+ const currentCohort = getCurrentCohort(state.cohorts);
14375
+ if (currentCohort.saved && !state.cohorts.autoSaveInProgress.has(currentCohort.id)) {
14376
+ setTimeout(()=>{
14377
+ const updatedState = store.getState();
14378
+ const updatedCohort = getCurrentCohort(updatedState.cohorts);
14379
+ if (updatedCohort.saved && !updatedState.cohorts.autoSaveInProgress.has(updatedCohort.id)) {
14380
+ store.dispatch(autoSaveCohort(updatedCohort.id));
14381
+ }
14382
+ }, 1000);
14383
+ }
14384
+ }
14385
+ return result;
14386
+ };
14387
+
14388
+ /* eslint @typescript-eslint/no-unused-vars: 0 */ // Credit to: https://github.com/vercel/next.js/discussions/15687
14389
+ const createNoopStorage = ()=>{
14390
+ return {
14391
+ getItem (_key) {
14392
+ return Promise.resolve(null);
14393
+ },
14394
+ setItem (_key, value) {
14395
+ return Promise.resolve(value);
14396
+ },
14397
+ removeItem (_key) {
14398
+ return Promise.resolve();
14399
+ }
14400
+ };
14401
+ };
14402
+ const storage = typeof window !== 'undefined' ? createWebStorage('local') : createNoopStorage();
14403
+ typeof window !== 'undefined' ? createWebStorage('session') : createNoopStorage();
14404
+
14405
+ // Create the entity adapter
14406
+ const dataAccessRequestsAdapter = toolkit.createEntityAdapter({
14407
+ // Select the id as the primary key
14408
+ selectId: (request)=>request.id,
14409
+ // Optional: Sort by request_datetime descending (newest first)
14410
+ sortComparer: (a, b)=>new Date(b.createdDatetime).getTime() - new Date(a.createdDatetime).getTime()
14411
+ });
14412
+ // Create a slice with reducers
14413
+ const dataAccessRequestsSlice = toolkit.createSlice({
14414
+ name: 'dataAccessRequests',
14415
+ initialState: dataAccessRequestsAdapter.getInitialState(),
14416
+ reducers: {
14417
+ addDataAccessRequest: (state, action)=>{
14418
+ const { cohortId, status, id, name, email, organization, createdDatetime, updatedDatetime } = action.payload;
14419
+ const dataAccessRequests = newDataAccessRequest(id, status, {
14420
+ name,
14421
+ email,
14422
+ organization
14423
+ }, cohortId, createdDatetime, updatedDatetime);
14424
+ dataAccessRequestsAdapter.addOne(state, dataAccessRequests);
14425
+ },
14426
+ // Set a request's status
14427
+ setRequestStatus: (state, action)=>{
14428
+ const { id, status } = action.payload;
14429
+ dataAccessRequestsAdapter.updateOne(state, {
14430
+ id,
14431
+ changes: {
14432
+ status
14433
+ }
14434
+ });
14435
+ }
14436
+ }
14437
+ });
14438
+ // Export actions
14439
+ const { addDataAccessRequest, setRequestStatus } = dataAccessRequestsSlice.actions;
14440
+ // Export selectors
14441
+ const { selectAll: selectAllDataAccessRequests, selectById: selectDataAccessRequestById, selectIds: selectDataAccessRequestIds, selectEntities: selectDataAccessRequestEntities, selectTotal: selectTotalDataAccessRequests } = dataAccessRequestsAdapter.getSelectors((state)=>state.dataAccessRequests);
14442
+ const selectCohortToRequestId = toolkit.createSelector([
14443
+ selectAllDataAccessRequests
14444
+ ], (requests)=>{
14445
+ return requests.reduce((acc, request)=>{
14446
+ acc[request.cohortId] = request.id;
14447
+ return acc;
14448
+ }, {});
14449
+ });
14450
+ // Export reducer
14451
+ const dataAccessRequestsReducer = dataAccessRequestsSlice.reducer;
14452
+
14453
+ const _APP_NAME$3 = 'CohortDiscovery';
14454
+ const persistConfig = {
14455
+ key: _APP_NAME$3,
14456
+ version: 1,
14457
+ storage
14458
+ };
14459
+ // create the store, context and selector for the ProjectsCenter
14460
+ // Note the project app has a local store and context which isolates
14461
+ // the filters and other store/cache values
14462
+ const reducers = redux.combineReducers({
14463
+ selectedIndexFacets: selectedFacetsReducer,
14464
+ filtersExpandedState: filtersExpandedReducer,
14465
+ facetDefinitionState: facetDefinitionsReducer,
14466
+ cohorts: cohortManagerReducer,
14467
+ // savedCohorts: saveCohortPersistenceReducer,
14468
+ dataAccessRequests: dataAccessRequestsReducer
14469
+ });
14470
+ const persistedReducers = reduxPersist.persistReducer(persistConfig, reducers);
14471
+ const { id, AppStore, AppContext, useAppSelector, useAppDispatch } = core.createAppStore({
14472
+ reducers: persistedReducers,
14473
+ middleware: autoSaveMiddleware,
14474
+ name: _APP_NAME$3,
14475
+ version: '0.0.1'
14476
+ });
14477
+
14478
+ // Selectors
14479
+ const { selectAll: selectAllCohorts, selectTotal: selectTotalCohorts, selectById: selectCohortById, selectIds: selectCohortIds } = cohortsAdapter.getSelectors((state)=>state.cohorts);
14480
+ const selectCurrentCohortId = (state)=>state.cohorts.currentCohortId;
14481
+ const selectCurrentCohort = (state)=>{
14482
+ const { currentCohortId, entities } = state.cohorts;
14483
+ const cohort = entities[currentCohortId];
14484
+ if (!cohort) {
14485
+ return createDefaultCohort();
14486
+ }
14487
+ return cohort;
14488
+ };
14489
+ toolkit.createSelector([
14490
+ selectAllCohorts
14491
+ ], (cohorts)=>cohorts.filter((cohort)=>cohort.saved));
14492
+ toolkit.createSelector([
14493
+ selectAllCohorts
14494
+ ], (cohorts)=>cohorts.filter((cohort)=>!cohort.saved));
14495
+ toolkit.createSelector([
14496
+ selectAllCohorts
14497
+ ], (cohorts)=>cohorts.filter((cohort)=>cohort.modified && !cohort.saved));
14498
+ toolkit.createSelector([
14499
+ (state)=>Array.from(state.cohorts.autoSaveInProgress)
14500
+ ], (autoSaveArray)=>autoSaveArray);
14501
+ toolkit.createSelector([
14502
+ (state)=>state.cohorts.autoSaveInProgress,
14503
+ (state, cohortId)=>cohortId
14504
+ ], (autoSaveArray, cohortId)=>autoSaveArray.includes(cohortId));
14505
+ const selectCurrentCohortName = toolkit.createSelector([
14506
+ selectCurrentCohort
14507
+ ], (cohort)=>cohort.name);
14508
+ const selectCurrentCohortFilters = toolkit.createSelector([
14509
+ selectCurrentCohort
14510
+ ], (cohort)=>cohort.filters);
14511
+ const selectCurrentCohortIndexFilters = toolkit.createSelector([
14512
+ selectCurrentCohort,
14513
+ (state, index)=>index
14514
+ ], (cohort, index)=>{
14515
+ return cohort.filters?.[index] ?? EmptyFilterSet;
14516
+ });
14517
+ toolkit.createSelector([
14518
+ selectAllCohorts,
14519
+ (state, name, excludeId)=>({
14520
+ name,
14521
+ excludeId
14522
+ })
14523
+ ], (cohorts, params)=>{
14524
+ const trimmedName = params.name.trim().toLowerCase();
14525
+ return cohorts.some((cohort)=>cohort.id !== params.excludeId && cohort.name.trim().toLowerCase() === trimmedName);
14526
+ });
14527
+ const selectCohortManagerLoading = (state)=>state.cohorts.loading;
14528
+ const selectCohortIdToNameMap = toolkit.createSelector([
14529
+ selectAllCohorts
14530
+ ], (cohorts)=>{
14531
+ return cohorts.reduce((acc, cohort)=>{
14532
+ acc[cohort.id] = cohort.name;
14533
+ return acc;
14534
+ }, {});
14535
+ });
14536
+
14537
+ const useToggleExpandFilter = ()=>{
14538
+ const dispatch = useAppDispatch();
14539
+ return (field, expanded)=>{
14540
+ dispatch(toggleCategoryFilter({
14541
+ field,
14542
+ expanded
14543
+ }));
14544
+ };
14545
+ };
14546
+ const useFilterExpandedState = (field)=>{
14547
+ return useAppSelector((state)=>selectFilterExpanded(state, field));
14548
+ };
14549
+ const useCohortFacetFilters = (index)=>{
14550
+ return useAppSelector((state)=>selectCurrentCohortFilters(state)[index]);
14551
+ };
14552
+ const useClearFilters = (index)=>{
14553
+ const dispatch = useAppDispatch();
14554
+ return (field)=>{
14555
+ dispatch(removeCohortFilter({
14556
+ index,
14557
+ field
14558
+ }));
14559
+ };
14560
+ };
14561
+ /**
14562
+ * Update Guppy filters: process nested fields and have the final
14563
+ * leaf be filtered
14564
+ * @param index
14565
+ */ const useUpdateFilters = (index)=>{
14566
+ const dispatch = useAppDispatch();
14567
+ // update the filter for this facet
14568
+ return (field, filter)=>{
14569
+ dispatch(updateCohortFilter({
14570
+ index: index,
14571
+ field: field,
14572
+ filter: buildNested(field, filter)
13856
14573
  }));
13857
14574
  };
13858
14575
  };
13859
14576
  const useGetFacetFilters = (index, field)=>{
13860
- return useAppSelector((state)=>selectIndexedFilterByName(state, index, field) ?? {
13861
- operator: 'and',
13862
- operands: []
13863
- });
14577
+ return useAppSelector(selectCurrentCohortIndexFilters(index, field) ?? {
14578
+ operator: 'and',
14579
+ operands: []
14580
+ });
13864
14581
  };
13865
14582
 
13866
14583
  const EnumFacetPanel = ({ facet, hooks, valueLabel, showTotals = false, chartType = 'bar' })=>{
@@ -13879,12 +14596,12 @@ const EnumFacetPanel = ({ facet, hooks, valueLabel, showTotals = false, chartTyp
13879
14596
  label,
13880
14597
  field
13881
14598
  ]);
13882
- //const { data } = hooks.useGetFacetData(field);
13883
14599
  const chart = createChart(chartType, {
13884
14600
  data: visibleData,
13885
14601
  total: 1,
13886
14602
  valueType: 'count'
13887
14603
  });
14604
+ const clearFilters = hooks.useClearFilter();
13888
14605
  return /*#__PURE__*/ jsxRuntime.jsxs("div", {
13889
14606
  className: "flex flex-col bg-base-max relative border-base border-1 rounded-b-md transition",
13890
14607
  children: [
@@ -13896,9 +14613,26 @@ const EnumFacetPanel = ({ facet, hooks, valueLabel, showTotals = false, chartTyp
13896
14613
  fw: 600,
13897
14614
  children: facetName
13898
14615
  }),
13899
- showTotals ? /*#__PURE__*/ jsxRuntime.jsx(core$1.Text, {
13900
- children: "Todo"
13901
- }) : null
14616
+ /*#__PURE__*/ jsxRuntime.jsxs("div", {
14617
+ className: "flex nowrap",
14618
+ children: [
14619
+ showTotals ? /*#__PURE__*/ jsxRuntime.jsx(core$1.Text, {
14620
+ children: "Todo"
14621
+ }) : null,
14622
+ /*#__PURE__*/ jsxRuntime.jsx(core$1.Tooltip, {
14623
+ label: "Clear selection",
14624
+ children: /*#__PURE__*/ jsxRuntime.jsx(FacetIconButton, {
14625
+ onClick: ()=>clearFilters(field),
14626
+ "aria-label": "clear selection",
14627
+ color: "accent.4",
14628
+ children: /*#__PURE__*/ jsxRuntime.jsx(fa.FaUndo, {
14629
+ size: "1.0em",
14630
+ "aria-hidden": "true"
14631
+ })
14632
+ })
14633
+ })
14634
+ ]
14635
+ })
13902
14636
  ]
13903
14637
  }),
13904
14638
  /*#__PURE__*/ jsxRuntime.jsx("div", {
@@ -14377,7 +15111,7 @@ const NUM_COLS = 2; // TODO: add to config
14377
15111
  * @param {Array} facets - The list of facets to be rendered.
14378
15112
  * @returns {JSX.Element} The rendered component showing facets.
14379
15113
  */ const ChartsAndFacetsPanel = ({ index, facets })=>{
14380
- const cohortFilters = useAppSelector((state)=>selectIndexFilters(state, index));
15114
+ const cohortFilters = useAppSelector((state)=>selectCurrentCohortIndexFilters(state, index));
14381
15115
  const { data, isSuccess} = useRoundedAggsQuery({
14382
15116
  type: index,
14383
15117
  fields: facets.map((x)=>x.field),
@@ -14449,105 +15183,483 @@ const NUM_COLS = 2; // TODO: add to config
14449
15183
  });
14450
15184
  };
14451
15185
 
14452
- const SaveActionButton = ({ index })=>{
14453
- const [opened, { close, open }] = hooks.useDisclosure(false);
14454
- const filters = useAppSelector((state)=>selectCohortFilters(state));
14455
- const [cohortName, setCohortName] = React.useState('New Cohort');
14456
- const appDispatch = useAppDispatch();
14457
- return /*#__PURE__*/ jsxRuntime.jsxs(jsxRuntime.Fragment, {
15186
+ const IconSize = {
15187
+ xs: 14,
15188
+ sm: 16,
15189
+ md: 20,
15190
+ lg: 24,
15191
+ xl: 32,
15192
+ 'xl-2': 36
15193
+ };
15194
+
15195
+ const DataAccessRequestForm = ({ cohortId, submitFunction, close })=>{
15196
+ const [submitted, setSubmitted] = React.useState(false);
15197
+ const form$1 = form.useForm({
15198
+ initialValues: {
15199
+ name: '',
15200
+ organization: '',
15201
+ email: ''
15202
+ },
15203
+ validate: {
15204
+ name: form.isNotEmpty('Name is required'),
15205
+ email: form.isEmail('Please provide a valid email')
15206
+ }
15207
+ });
15208
+ const handleSubmit = async (values)=>{
15209
+ submitFunction(cohortId, values);
15210
+ setSubmitted(true);
15211
+ };
15212
+ if (submitted) {
15213
+ return /*#__PURE__*/ jsxRuntime.jsxs(core$1.Paper, {
15214
+ shadow: "md",
15215
+ p: "xl",
15216
+ radius: "md",
15217
+ children: [
15218
+ /*#__PURE__*/ jsxRuntime.jsx(core$1.Title, {
15219
+ order: 2,
15220
+ mb: "md",
15221
+ ta: "center",
15222
+ children: "Request Submitted"
15223
+ }),
15224
+ /*#__PURE__*/ jsxRuntime.jsx(core$1.Text, {
15225
+ ta: "center",
15226
+ children: "Thank you for your data access request. We will review your submission and contact you shortly."
15227
+ }),
15228
+ /*#__PURE__*/ jsxRuntime.jsx(core$1.Group, {
15229
+ justify: "center",
15230
+ mt: "xl",
15231
+ children: /*#__PURE__*/ jsxRuntime.jsx(core$1.Button, {
15232
+ onClick: ()=>close(),
15233
+ children: "Dismiss"
15234
+ })
15235
+ })
15236
+ ]
15237
+ });
15238
+ }
15239
+ return /*#__PURE__*/ jsxRuntime.jsxs(core$1.Paper, {
15240
+ radius: "md",
14458
15241
  children: [
14459
- /*#__PURE__*/ jsxRuntime.jsx(core$1.Modal, {
14460
- opened: opened,
14461
- onClose: close,
14462
- radius: "md",
14463
- title: "Save Cohort",
14464
- classNames: {
14465
- header: 'p-1 px-4 border-base-lighter border-b-1 uppercase font-bold'
14466
- },
15242
+ /*#__PURE__*/ jsxRuntime.jsx(core$1.Text, {
15243
+ c: "dimmed",
15244
+ mb: "lg",
15245
+ children: "Please complete all fields to request access to our research data."
15246
+ }),
15247
+ /*#__PURE__*/ jsxRuntime.jsx(core$1.Divider, {
15248
+ mb: "lg"
15249
+ }),
15250
+ /*#__PURE__*/ jsxRuntime.jsx(core$1.Box, {
15251
+ component: "form",
15252
+ onSubmit: form$1.onSubmit(handleSubmit),
14467
15253
  children: /*#__PURE__*/ jsxRuntime.jsxs(core$1.Stack, {
14468
- className: "mt-2",
15254
+ gap: "md",
14469
15255
  children: [
14470
15256
  /*#__PURE__*/ jsxRuntime.jsx(core$1.TextInput, {
14471
- description: "Enter name to save cohort",
14472
- label: "Cohort Name",
14473
- placeholder: "New Cohort",
14474
- "data-autofocus": true,
14475
- onChange: (event)=>{
14476
- setCohortName(event.currentTarget.value);
14477
- },
14478
- value: cohortName,
14479
- required: true
15257
+ label: "Full Name",
15258
+ placeholder: "John Doe",
15259
+ withAsterisk: true,
15260
+ ...form$1.getInputProps('name')
15261
+ }),
15262
+ /*#__PURE__*/ jsxRuntime.jsx(core$1.TextInput, {
15263
+ label: "Organization",
15264
+ placeholder: "University, Organization, or Company",
15265
+ withAsterisk: true,
15266
+ ...form$1.getInputProps('organization')
15267
+ }),
15268
+ /*#__PURE__*/ jsxRuntime.jsx(core$1.TextInput, {
15269
+ label: "Email",
15270
+ placeholder: "your.email@institution.edu",
15271
+ withAsterisk: true,
15272
+ ...form$1.getInputProps('email')
14480
15273
  }),
14481
15274
  /*#__PURE__*/ jsxRuntime.jsxs(core$1.Group, {
14482
15275
  justify: "flex-end",
14483
- className: "bg-base-lighter -m-4 p-3 mt-3",
14484
15276
  children: [
14485
15277
  /*#__PURE__*/ jsxRuntime.jsx(core$1.Button, {
15278
+ type: "reset",
14486
15279
  variant: "outline",
14487
- onClick: close,
14488
- children: "Cancel"
15280
+ onClick: ()=>form$1.reset(),
15281
+ children: "Reset"
14489
15282
  }),
14490
15283
  /*#__PURE__*/ jsxRuntime.jsx(core$1.Button, {
14491
- onClick: ()=>{
14492
- appDispatch(saveCohort({
14493
- name: cohortName,
14494
- filters: filters
14495
- }));
14496
- close();
14497
- },
14498
- children: "Save"
15284
+ variant: "outline",
15285
+ onClick: ()=>close(),
15286
+ children: submitted ? 'Close' : 'Cancel'
15287
+ }),
15288
+ !submitted && /*#__PURE__*/ jsxRuntime.jsx(core$1.Button, {
15289
+ type: "submit",
15290
+ children: "Submit Request"
14499
15291
  })
14500
15292
  ]
14501
15293
  })
14502
15294
  ]
14503
15295
  })
15296
+ })
15297
+ ]
15298
+ });
15299
+ };
15300
+
15301
+ class QueryAllIndexedError extends Error {
15302
+ constructor(message){
15303
+ super(message);
15304
+ this.name = 'QueryAllIndexedError';
15305
+ }
15306
+ }
15307
+ /**
15308
+ * Queries all resources based on the provided filters and resource fields, executing an aggregation query for each resource type.
15309
+ *
15310
+ * @async
15311
+ * @function
15312
+ * @param {IndexedFilterSet} filters - A set of filters indexed by resource types, used to refine the query results for each resource type.
15313
+ * @param {IndexResourceField} resourceFields - A mapping of resource types to their respective field configurations, including resource paths and field details.
15314
+ * @param {TriggerFunctionType} triggerAggsQuery - A function that triggers the aggregation query for a specific resource type and its associated fields.
15315
+ * @param {string[]} [excludeList=['', 'no data']] - An optional list of strings to exclude from the results, with default values set to an empty string and "no data".
15316
+ * @returns {Promise<string[]>} A promise that resolves to an array of resource paths for all queried and processed resources.
15317
+ * @throws {QueryAllIndexedError} Throws an error if the query operation fails or an unknown error occurs during execution.
15318
+ */ const queryAllResources = async (filters, resourceFields, triggerAggsQuery, excludeList = [
15319
+ '',
15320
+ 'no data'
15321
+ ])=>{
15322
+ try {
15323
+ const promises = Object.entries(resourceFields).map(([index, fieldConfig])=>triggerAggsQuery({
15324
+ filters: filters[index],
15325
+ type: index,
15326
+ fields: [
15327
+ fieldConfig.resourceField
15328
+ ],
15329
+ accessibility: core.Accessibility.ALL
15330
+ }).unwrap().then((result)=>({
15331
+ index,
15332
+ result
15333
+ })));
15334
+ const resources = await Promise.all(promises);
15335
+ const resultsByIndex = resources.reduce((acc, resource)=>{
15336
+ const filteredResults = Object.values(resource.result).map((hist)=>hist.map((h)=>h.key)).flat().filter((x)=>typeof x === 'string' && !excludeList.includes(x));
15337
+ const uniqueResults = new Set(filteredResults);
15338
+ const stringResults = Array.from(uniqueResults);
15339
+ acc[resource.index] = stringResults;
15340
+ return acc;
15341
+ }, {});
15342
+ return Object.entries(resultsByIndex).reduce((acc, [index, results])=>{
15343
+ const resourcePath = resourceFields[index].resourcePath;
15344
+ if (resourcePath) {
15345
+ acc.push(...results.map((x)=>`${resourcePath}/${x}`));
15346
+ } else {
15347
+ acc.push(...results.map((x)=>`/${x}`));
15348
+ }
15349
+ return acc;
15350
+ }, []);
15351
+ } catch (error) {
15352
+ if (error instanceof Error) {
15353
+ throw new QueryAllIndexedError(error.message);
15354
+ } else {
15355
+ throw new QueryAllIndexedError('Unknown error');
15356
+ }
15357
+ }
15358
+ };
15359
+
15360
+ const REQUESTOR_HTTP_ERROR_MESSAGES = {
15361
+ 401: 'You are not authorized to request access to this data. Please contact the site administrator.',
15362
+ 403: 'You do not have the correct permissions to request this resource. Please contact the site administrator.'
15363
+ };
15364
+ const submitCohortRequestAction = async (cohort, values, user, indexResources, remoteSupportService, appDispatch, getAggs, createRequestQuery)=>{
15365
+ if (cohort && values) {
15366
+ // need to get the resources requires by the cohort
15367
+ try {
15368
+ // query for resources from
15369
+ const resources = await queryAllResources(cohort.filters, indexResources, getAggs);
15370
+ // now we have the resources, submit a requestor request
15371
+ const request = await createRequestQuery({
15372
+ resource_paths: resources
15373
+ }).unwrap();
15374
+ // followed by a zendesk request
15375
+ const zendeskRequestAction = core.getRemoteSupportServiceRegistry().getSupportService(remoteSupportService.service);
15376
+ await zendeskRequestAction({
15377
+ subject: `Request for access to ${cohort.name}`,
15378
+ fullName: `${values.name}`,
15379
+ email: `${values.email}`,
15380
+ contents: `Requestor: ${values.name} (${values.email})` + `\n\nCohort: ${cohort.name}` + `\n\nResources: ${resources.join(', ')}` + `\n\nRequestor ID: ${user?.username || 'unknown'}` + `\n\nRequest ID: ${request.request_id}` + `\n\nRequest URL: ${window.location.href}` + `\n\nRequestor Email: ${values.email}` + `\n\nRequestor Name: ${values.name}` + `\n\nRequestor Organization: ${values.organization}`
15381
+ }, remoteSupportService.configuration);
15382
+ // update the request store
15383
+ appDispatch(addDataAccessRequest({
15384
+ cohortId: cohort.id,
15385
+ id: request.request_id ?? 'unknown',
15386
+ name: values.name,
15387
+ email: values.email,
15388
+ status: request.status ?? 'unknown',
15389
+ organization: values.organization,
15390
+ createdDatetime: request.created_time ?? 'unknown',
15391
+ updatedDatetime: request.updated_time ?? 'unknown'
15392
+ }));
15393
+ // Close the modal after a successful dispatch
15394
+ close();
15395
+ } catch (error) {
15396
+ if (error instanceof QueryAllIndexedError) {
15397
+ notifications.notifications.show({
15398
+ title: 'Request Cohort',
15399
+ message: `Error while getting resources for cohort: ${error.message}`
15400
+ });
15401
+ } else if (core.isHttpStatusError(error)) {
15402
+ const httpError = error;
15403
+ notifications.notifications.show({
15404
+ title: 'Request Cohort',
15405
+ message: REQUESTOR_HTTP_ERROR_MESSAGES[httpError.status] || `Error while submitting resource request for cohorts`
15406
+ });
15407
+ } else if (error instanceof core.MissingServiceConfigurationError) {
15408
+ notifications.notifications.show({
15409
+ title: 'Request Cohort',
15410
+ message: `Error while submitting resource request for cohorts: ${error.message}`
15411
+ });
15412
+ } else if (error instanceof Error) {
15413
+ notifications.notifications.show({
15414
+ title: 'Request Cohort',
15415
+ message: `Error while submitting resource request for cohorts: ${error.message}`
15416
+ });
15417
+ } else {
15418
+ notifications.notifications.show({
15419
+ title: 'Request Cohort',
15420
+ message: 'Unknown error while submitting resource request for cohorts'
15421
+ });
15422
+ }
15423
+ }
15424
+ }
15425
+ };
15426
+
15427
+ const CohortManager = ({ indexResources, remoteSupportService, size = 'md' })=>{
15428
+ const dispatch = useAppDispatch();
15429
+ const [isEditing, setIsEditing] = React.useState(false);
15430
+ const [editingLabel, setEditingLabel] = React.useState('');
15431
+ const allCohorts = useAppSelector(selectAllCohorts);
15432
+ const currentCohort = useAppSelector(selectCurrentCohort);
15433
+ const loading = useAppSelector(selectCohortManagerLoading);
15434
+ const appDispatch = useAppDispatch();
15435
+ const [getGetAggs] = core.useLazyGetAggsNoFilterSelfQuery();
15436
+ const [requestQuery] = core.useCreateRequestMutation();
15437
+ const user = core.useCoreSelector(core.selectUserDetails);
15438
+ const theme = core$1.useMantineTheme();
15439
+ const iconSize = IconSize[size] || IconSize['sm'];
15440
+ const startEditing = ()=>{
15441
+ setIsEditing(true);
15442
+ setEditingLabel(currentCohort.name);
15443
+ };
15444
+ const hasNoFilters = currentCohort ? isIndexedFilterSetEmpty(currentCohort?.filters) : true;
15445
+ const cancelEditing = ()=>{
15446
+ setIsEditing(false);
15447
+ setEditingLabel('');
15448
+ };
15449
+ const saveEdit = ()=>{
15450
+ if (editingLabel.trim() === '' || !currentCohort.name) return;
15451
+ dispatch(updateCohortName({
15452
+ id: currentCohort.id,
15453
+ name: editingLabel
15454
+ }));
15455
+ setIsEditing(false);
15456
+ setEditingLabel('');
15457
+ };
15458
+ const handleCreateNew = ()=>{
15459
+ dispatch(createNewCohort({
15460
+ name: 'New Cohort'
15461
+ }));
15462
+ };
15463
+ const selectData = React.useMemo(()=>{
15464
+ return allCohorts.map((cohort)=>({
15465
+ label: cohort.name,
15466
+ value: cohort.id
15467
+ }));
15468
+ }, [
15469
+ allCohorts
15470
+ ]);
15471
+ const onSelectCohort = React.useCallback((_value, option)=>{
15472
+ if (option) {
15473
+ dispatch(setCurrentCohortId(option.value));
15474
+ }
15475
+ }, [
15476
+ dispatch
15477
+ ]);
15478
+ const handleCohortAccessRequest = async (cohortId, values)=>{
15479
+ const cohort = allCohorts.find((obj)=>obj.id === cohortId);
15480
+ if (cohort) await submitCohortRequestAction(cohort, values, user, indexResources, remoteSupportService, appDispatch, getGetAggs, requestQuery);
15481
+ };
15482
+ if (loading) {
15483
+ return /*#__PURE__*/ jsxRuntime.jsx("div", {
15484
+ children: "Loading cohorts..."
15485
+ });
15486
+ }
15487
+ return /*#__PURE__*/ jsxRuntime.jsxs(core$1.Group, {
15488
+ gap: "xs",
15489
+ className: "flex pt-4 pl-2",
15490
+ children: [
15491
+ !isEditing ? /*#__PURE__*/ jsxRuntime.jsxs(core$1.Group, {
15492
+ children: [
15493
+ /*#__PURE__*/ jsxRuntime.jsx(core$1.Select, {
15494
+ data: selectData,
15495
+ value: currentCohort?.id ?? null,
15496
+ onChange: onSelectCohort
15497
+ }),
15498
+ /*#__PURE__*/ jsxRuntime.jsx(core$1.Tooltip, {
15499
+ label: "Rename selected cohort",
15500
+ position: "bottom",
15501
+ withArrow: true,
15502
+ children: /*#__PURE__*/ jsxRuntime.jsx(core$1.Button, {
15503
+ variant: "action",
15504
+ color: "secondary.4",
15505
+ onClick: startEditing,
15506
+ title: `Rename ${currentCohort?.name}`,
15507
+ "data-testid": "renameCohort",
15508
+ "aria-label": "Rename cohort",
15509
+ size: `compact-${size}`,
15510
+ children: /*#__PURE__*/ jsxRuntime.jsx(react.Icon, {
15511
+ icon: "gen3:edit",
15512
+ height: iconSize,
15513
+ width: iconSize
15514
+ })
15515
+ })
15516
+ })
15517
+ ]
15518
+ }) : /*#__PURE__*/ jsxRuntime.jsxs(core$1.Group, {
15519
+ gap: "xs",
15520
+ children: [
15521
+ /*#__PURE__*/ jsxRuntime.jsx(core$1.TextInput, {
15522
+ value: editingLabel,
15523
+ onChange: (e)=>setEditingLabel(e.target.value),
15524
+ placeholder: "Enter new name",
15525
+ onKeyDown: (e)=>{
15526
+ if (e.key === 'Enter' && editingLabel.trim()) saveEdit();
15527
+ if (e.key === 'Escape') cancelEditing();
15528
+ },
15529
+ autoFocus: true,
15530
+ style: {
15531
+ flex: 1
15532
+ }
15533
+ }),
15534
+ /*#__PURE__*/ jsxRuntime.jsx(core$1.Tooltip, {
15535
+ label: "Apply rename",
15536
+ position: "bottom",
15537
+ withArrow: true,
15538
+ children: /*#__PURE__*/ jsxRuntime.jsx(core$1.Button, {
15539
+ variant: "action",
15540
+ color: "secondary.4",
15541
+ onClick: saveEdit,
15542
+ "data-testid": "completeCohortRenameButton",
15543
+ "aria-label": "Complete rename cohort",
15544
+ disabled: editingLabel.trim() === '' || editingLabel.trim() === currentCohort.name,
15545
+ size: `compact-${size}`,
15546
+ children: /*#__PURE__*/ jsxRuntime.jsx(react.Icon, {
15547
+ icon: "gen3:check",
15548
+ height: iconSize,
15549
+ width: iconSize
15550
+ })
15551
+ })
15552
+ }),
15553
+ /*#__PURE__*/ jsxRuntime.jsx(core$1.Tooltip, {
15554
+ label: "Cancel rename",
15555
+ position: "bottom",
15556
+ withArrow: true,
15557
+ children: /*#__PURE__*/ jsxRuntime.jsx(core$1.Button, {
15558
+ variant: "action",
15559
+ "data-testid": "cancelRenameButton",
15560
+ "aria-label": "Cancel rename cohort",
15561
+ color: theme.colors.accent[4],
15562
+ onClick: cancelEditing,
15563
+ size: `compact-${size}`,
15564
+ children: /*#__PURE__*/ jsxRuntime.jsx(md.MdClose, {
15565
+ size: "1.5em",
15566
+ "aria-hidden": "true"
15567
+ })
15568
+ })
15569
+ })
15570
+ ]
15571
+ }),
15572
+ /*#__PURE__*/ jsxRuntime.jsx(core$1.Tooltip, {
15573
+ label: "Create a new cohort",
15574
+ position: "bottom",
15575
+ withArrow: true,
15576
+ children: /*#__PURE__*/ jsxRuntime.jsx(core$1.Button, {
15577
+ size: `compact-${size}`,
15578
+ "data-testid": "addButton",
15579
+ "aria-label": "Add cohort",
15580
+ variant: "action",
15581
+ color: "secondary.4",
15582
+ onClick: handleCreateNew,
15583
+ children: /*#__PURE__*/ jsxRuntime.jsx(md.MdAdd, {
15584
+ size: "1.5em",
15585
+ "aria-hidden": "true"
15586
+ })
15587
+ })
14504
15588
  }),
14505
- /*#__PURE__*/ jsxRuntime.jsx(core$1.Button, {
14506
- size: "sm",
14507
- variant: "outline",
14508
- color: "secondary.4",
14509
- disabled: !(index in filters) || Object.keys(filters[index].root).length == 0,
14510
- classNames: {
14511
- root: 'bg-base-max'
14512
- },
14513
- onClick: open,
14514
- children: "Save Cohort"
14515
- })
14516
- ]
14517
- });
14518
- };
14519
-
14520
- const ActionButtonGroup = ({ index })=>{
14521
- return /*#__PURE__*/ jsxRuntime.jsxs(core$1.Group, {
14522
- gap: "xs",
14523
- className: "flex pt-4 pl-2",
14524
- children: [
14525
- /*#__PURE__*/ jsxRuntime.jsx(SaveActionButton, {
14526
- index: index
15589
+ /*#__PURE__*/ jsxRuntime.jsx(core$1.Tooltip, {
15590
+ label: "Delete selected cohort",
15591
+ position: "bottom",
15592
+ withArrow: true,
15593
+ children: /*#__PURE__*/ jsxRuntime.jsx(core$1.Button, {
15594
+ size: `compact-${size}`,
15595
+ "data-testid": "uploadButton",
15596
+ "aria-label": "Upload cohort",
15597
+ variant: "action",
15598
+ onClick: ()=>{
15599
+ modals.modals.openConfirmModal({
15600
+ title: `Delete Cohort: ${currentCohort?.name}`,
15601
+ centered: true,
15602
+ children: /*#__PURE__*/ jsxRuntime.jsxs(core$1.Text, {
15603
+ size: "sm",
15604
+ children: [
15605
+ "Are you sure you want to delete your cohort? Deleted cohorts cannot be restored.",
15606
+ ' ',
15607
+ currentCohort?.requests?.length > 0 ? `This cohort has ${currentCohort?.requests?.length} requests. Deleting this cohort will results in not being able to correlate nay pending requests.
15608
+ Are you sure you want to delete this cohort?.` : ''
15609
+ ]
15610
+ }),
15611
+ labels: {
15612
+ confirm: 'Delete Cohort',
15613
+ cancel: 'Cancel'
15614
+ },
15615
+ confirmProps: {
15616
+ color: theme.colors.accent[4]
15617
+ },
15618
+ onConfirm: ()=>{
15619
+ dispatch(removeCohort(currentCohort.id));
15620
+ }
15621
+ });
15622
+ },
15623
+ children: /*#__PURE__*/ jsxRuntime.jsx(react.Icon, {
15624
+ icon: "gen3:delete",
15625
+ height: iconSize,
15626
+ width: iconSize
15627
+ })
15628
+ })
14527
15629
  }),
14528
- /*#__PURE__*/ jsxRuntime.jsx(core$1.Button, {
14529
- size: "sm",
14530
- variant: "outline",
14531
- color: "secondary.4",
14532
- classNames: {
14533
- root: 'bg-base-max'
14534
- },
14535
- children: "Export Cohort"
15630
+ /*#__PURE__*/ jsxRuntime.jsx(core$1.Tooltip, {
15631
+ label: hasNoFilters ? 'No Filters defined. Cannot submit request.' : 'Submit Data Access Request',
15632
+ position: "bottom",
15633
+ withArrow: true,
15634
+ children: /*#__PURE__*/ jsxRuntime.jsx(core$1.Button, {
15635
+ size: `compact-${size}`,
15636
+ "data-testid": "cohortManager-requestCohortAccessButton",
15637
+ "aria-label": "Request cohort access",
15638
+ variant: "action",
15639
+ disabled: hasNoFilters,
15640
+ onClick: ()=>{
15641
+ const modelId = modals.modals.open({
15642
+ title: 'DATA ACCESS REQUEST',
15643
+ children: /*#__PURE__*/ jsxRuntime.jsx(DataAccessRequestForm, {
15644
+ cohortId: currentCohort.id,
15645
+ submitFunction: handleCohortAccessRequest,
15646
+ close: ()=>modals.modals.close(modelId)
15647
+ })
15648
+ });
15649
+ },
15650
+ children: /*#__PURE__*/ jsxRuntime.jsx(react.Icon, {
15651
+ icon: "gen3:request",
15652
+ height: iconSize,
15653
+ width: iconSize
15654
+ })
15655
+ })
14536
15656
  }),
14537
- /*#__PURE__*/ jsxRuntime.jsx(core$1.Button, {
14538
- size: "sm",
14539
- variant: "outline",
14540
- color: "secondary.4",
14541
- classNames: {
14542
- root: 'bg-base-max'
14543
- },
14544
- children: "Import Cohort"
14545
- })
15657
+ null
14546
15658
  ]
14547
15659
  });
14548
15660
  };
14549
15661
 
14550
- const CohortManager = ({ index })=>{
15662
+ const CohortQueryExpression = ({ index })=>{
14551
15663
  const currentCohortId = useAppSelector((state)=>selectCurrentCohortId(state));
14552
15664
  const currentCohortName = useAppSelector((state)=>selectCurrentCohortName(state));
14553
15665
  return /*#__PURE__*/ jsxRuntime.jsx("div", {
@@ -14594,7 +15706,7 @@ const CohortManager = ({ index })=>{
14594
15706
  });
14595
15707
  };
14596
15708
 
14597
- const IndexPanel = ({ dataConfig, tabs, tabTitle, emptySelection })=>{
15709
+ const IndexPanel = ({ dataConfig, tabs, tabTitle, emptySelection, indexResources, remoteSupportService })=>{
14598
15710
  const [activeFieldDefinitions, setActiveFieldDefinitions] = React.useState([]);
14599
15711
  const appDispatch = useAppDispatch();
14600
15712
  const index = dataConfig.dataType;
@@ -14619,7 +15731,7 @@ const IndexPanel = ({ dataConfig, tabs, tabTitle, emptySelection })=>{
14619
15731
  selectedFacets,
14620
15732
  facetDefinitions
14621
15733
  ]);
14622
- const cohortFilters = useAppSelector((state)=>selectIndexFilters(state, index));
15734
+ const cohortFilters = useAppSelector((state)=>selectCurrentCohortIndexFilters(state, index));
14623
15735
  const queryFields = categories.length === 0 ? fields : activeFieldDefinitions.map((x)=>x.field);
14624
15736
  const { data, isSuccess, isError: isAggsQueryError } = useRoundedAggsQuery({
14625
15737
  type: index,
@@ -14686,10 +15798,11 @@ const IndexPanel = ({ dataConfig, tabs, tabTitle, emptySelection })=>{
14686
15798
  }
14687
15799
  return /*#__PURE__*/ jsxRuntime.jsxs(core$1.Stack, {
14688
15800
  children: [
14689
- /*#__PURE__*/ jsxRuntime.jsx(CohortManager, {
15801
+ /*#__PURE__*/ jsxRuntime.jsx(CohortQueryExpression, {
14690
15802
  index: index
14691
15803
  }),
14692
15804
  /*#__PURE__*/ jsxRuntime.jsxs(core$1.Flex, {
15805
+ wrap: "nowrap",
14693
15806
  className: "flex h-full bg-base-light pb-4 ml-4",
14694
15807
  children: [
14695
15808
  /*#__PURE__*/ jsxRuntime.jsx(FacetSelectionPanel, {
@@ -14703,10 +15816,11 @@ const IndexPanel = ({ dataConfig, tabs, tabTitle, emptySelection })=>{
14703
15816
  }
14704
15817
  }),
14705
15818
  /*#__PURE__*/ jsxRuntime.jsxs(core$1.Stack, {
14706
- className: "w-2/3 mr-2 min-h-[500px]",
15819
+ className: "w-full md:w-[40rem] lg:w-[50rem] xl:w-[60rem] mr-2 min-h-[500px]",
14707
15820
  children: [
14708
- /*#__PURE__*/ jsxRuntime.jsx(ActionButtonGroup, {
14709
- index: index
15821
+ /*#__PURE__*/ jsxRuntime.jsx(CohortManager, {
15822
+ indexResources: indexResources,
15823
+ remoteSupportService: remoteSupportService
14710
15824
  }),
14711
15825
  selectedFacets.length > 0 ? /*#__PURE__*/ jsxRuntime.jsx(ChartsAndFacetsPanel, {
14712
15826
  index: index,
@@ -14741,147 +15855,6 @@ const IndexPanel = ({ dataConfig, tabs, tabTitle, emptySelection })=>{
14741
15855
  });
14742
15856
  };
14743
15857
 
14744
- const IconSize = {
14745
- xs: 14,
14746
- sm: 16,
14747
- md: 20,
14748
- lg: 24,
14749
- xl: 32,
14750
- 'xl-2': 36
14751
- };
14752
-
14753
- const DataAccessRequestForm = ({ cohortId, submitFunction, close })=>{
14754
- const [submitted, setSubmitted] = React.useState(false);
14755
- const form$1 = form.useForm({
14756
- initialValues: {
14757
- name: '',
14758
- institution: '',
14759
- department: '',
14760
- address: '',
14761
- email: '',
14762
- phone: ''
14763
- },
14764
- validate: {
14765
- name: form.isNotEmpty('Name is required'),
14766
- institution: form.isNotEmpty('Institution is required'),
14767
- address: form.isNotEmpty('Address is required'),
14768
- email: form.isEmail('Please provide a valid email'),
14769
- phone: form.matches(/^[+]?[(]?[0-9]{3}[)]?[-\s.]?[0-9]{3}[-\s.]?[0-9]{4,6}$/, 'Please provide a valid phone number')
14770
- }
14771
- });
14772
- const handleSubmit = async (values)=>{
14773
- submitFunction(cohortId, values);
14774
- setSubmitted(true);
14775
- };
14776
- if (submitted) {
14777
- return /*#__PURE__*/ jsxRuntime.jsxs(core$1.Paper, {
14778
- shadow: "md",
14779
- p: "xl",
14780
- radius: "md",
14781
- children: [
14782
- /*#__PURE__*/ jsxRuntime.jsx(core$1.Title, {
14783
- order: 2,
14784
- mb: "md",
14785
- ta: "center",
14786
- children: "Request Submitted"
14787
- }),
14788
- /*#__PURE__*/ jsxRuntime.jsx(core$1.Text, {
14789
- ta: "center",
14790
- children: "Thank you for your data access request. We will review your submission and contact you shortly."
14791
- }),
14792
- /*#__PURE__*/ jsxRuntime.jsx(core$1.Group, {
14793
- justify: "center",
14794
- mt: "xl",
14795
- children: /*#__PURE__*/ jsxRuntime.jsx(core$1.Button, {
14796
- onClick: ()=>close(),
14797
- children: "Dismiss"
14798
- })
14799
- })
14800
- ]
14801
- });
14802
- }
14803
- return /*#__PURE__*/ jsxRuntime.jsxs(core$1.Paper, {
14804
- radius: "md",
14805
- children: [
14806
- /*#__PURE__*/ jsxRuntime.jsx(core$1.Text, {
14807
- c: "dimmed",
14808
- mb: "lg",
14809
- children: "Please complete all fields to request access to our research data."
14810
- }),
14811
- /*#__PURE__*/ jsxRuntime.jsx(core$1.Divider, {
14812
- mb: "lg"
14813
- }),
14814
- /*#__PURE__*/ jsxRuntime.jsx(core$1.Box, {
14815
- component: "form",
14816
- onSubmit: form$1.onSubmit(handleSubmit),
14817
- children: /*#__PURE__*/ jsxRuntime.jsxs(core$1.Stack, {
14818
- gap: "md",
14819
- children: [
14820
- /*#__PURE__*/ jsxRuntime.jsx(core$1.TextInput, {
14821
- label: "Full Name",
14822
- placeholder: "John Doe",
14823
- withAsterisk: true,
14824
- ...form$1.getInputProps('name')
14825
- }),
14826
- /*#__PURE__*/ jsxRuntime.jsx(core$1.TextInput, {
14827
- label: "Institution",
14828
- placeholder: "University, Organization, or Company",
14829
- withAsterisk: true,
14830
- ...form$1.getInputProps('institution')
14831
- }),
14832
- /*#__PURE__*/ jsxRuntime.jsx(core$1.TextInput, {
14833
- label: "Department",
14834
- placeholder: "Research Department",
14835
- ...form$1.getInputProps('department')
14836
- }),
14837
- /*#__PURE__*/ jsxRuntime.jsx(core$1.Textarea, {
14838
- label: "Address",
14839
- placeholder: "Full institutional address",
14840
- withAsterisk: true,
14841
- autosize: true,
14842
- minRows: 2,
14843
- maxRows: 4,
14844
- ...form$1.getInputProps('address')
14845
- }),
14846
- /*#__PURE__*/ jsxRuntime.jsx(core$1.TextInput, {
14847
- label: "Email",
14848
- placeholder: "your.email@institution.edu",
14849
- withAsterisk: true,
14850
- ...form$1.getInputProps('email')
14851
- }),
14852
- /*#__PURE__*/ jsxRuntime.jsx(core$1.TextInput, {
14853
- label: "Phone",
14854
- placeholder: "+1 (123) 456-7890",
14855
- withAsterisk: true,
14856
- ...form$1.getInputProps('phone')
14857
- }),
14858
- /*#__PURE__*/ jsxRuntime.jsxs(core$1.Group, {
14859
- justify: "flex-end",
14860
- children: [
14861
- /*#__PURE__*/ jsxRuntime.jsx(core$1.Button, {
14862
- type: "reset",
14863
- variant: "outline",
14864
- onClick: ()=>form$1.reset(),
14865
- children: "Reset"
14866
- }),
14867
- /*#__PURE__*/ jsxRuntime.jsx(core$1.Button, {
14868
- variant: "outline",
14869
- onClick: ()=>close(),
14870
- children: submitted ? 'Close' : 'Cancel'
14871
- }),
14872
- !submitted && /*#__PURE__*/ jsxRuntime.jsx(core$1.Button, {
14873
- type: "submit",
14874
- children: "Submit Request"
14875
- })
14876
- ]
14877
- })
14878
- ]
14879
- })
14880
- })
14881
- ]
14882
- });
14883
- };
14884
-
14885
15858
  /**
14886
15859
  * Formats a given date string into a specific format.
14887
15860
  *
@@ -14990,8 +15963,11 @@ const commonTableSettings$1 = (size = 'sm')=>{
14990
15963
  };
14991
15964
  };
14992
15965
 
14993
- const SavedCohortsTable = ({ size = 'md' })=>{
15966
+ const SavedCohortsTable = ({ indexResources, remoteSupportService, size = 'md' })=>{
14994
15967
  const appDispatch = useAppDispatch();
15968
+ const [getGetAggs] = core.useLazyGetAggsNoFilterSelfQuery();
15969
+ const [requestQuery] = core.useCreateRequestMutation();
15970
+ const user = core.useCoreSelector(core.selectUserDetails);
14995
15971
  const columns = React.useMemo(()=>[
14996
15972
  {
14997
15973
  accessorKey: 'name',
@@ -15004,7 +15980,7 @@ const SavedCohortsTable = ({ size = 'md' })=>{
15004
15980
  })
15005
15981
  },
15006
15982
  {
15007
- accessorKey: 'created_datetime',
15983
+ accessorKey: 'createdDatetime',
15008
15984
  header: 'Modified Date',
15009
15985
  Cell: ({ cell })=>/*#__PURE__*/ jsxRuntime.jsxs(core$1.Text, {
15010
15986
  children: [
@@ -15014,7 +15990,7 @@ const SavedCohortsTable = ({ size = 'md' })=>{
15014
15990
  })
15015
15991
  },
15016
15992
  {
15017
- accessorKey: 'modified_datetime',
15993
+ accessorKey: 'modifiedDatetime',
15018
15994
  header: 'Created Date',
15019
15995
  Cell: ({ cell })=>/*#__PURE__*/ jsxRuntime.jsxs(core$1.Text, {
15020
15996
  children: [
@@ -15045,15 +16021,9 @@ const SavedCohortsTable = ({ size = 'md' })=>{
15045
16021
  data,
15046
16022
  requestByCohortId
15047
16023
  ]);
15048
- const handleSubmission = (cohortId, values)=>{
15049
- if (cohortId && values) {
15050
- appDispatch(addDataAccessRequest({
15051
- cohortId,
15052
- userAccessInformation: values
15053
- }));
15054
- // Close the modal after successful dispatch
15055
- close();
15056
- }
16024
+ const handleCohortAccessRequest = async (cohortId, values)=>{
16025
+ const cohort = data.find((obj)=>obj.id === cohortId);
16026
+ if (cohort) await submitCohortRequestAction(cohort, values, user, indexResources, remoteSupportService, appDispatch, getGetAggs, requestQuery);
15057
16027
  };
15058
16028
  const table = mantineReactTable.useMantineReactTable({
15059
16029
  columns,
@@ -15061,23 +16031,26 @@ const SavedCohortsTable = ({ size = 'md' })=>{
15061
16031
  getRowId: (originalRow)=>originalRow.id,
15062
16032
  ...commonTableSettings$1(),
15063
16033
  positionActionsColumn: 'last',
15064
- renderRowActions: ({ row })=>/*#__PURE__*/ jsxRuntime.jsxs(core$1.Group, {
16034
+ renderRowActions: ({ row })=>{
16035
+ const hasNoFilters = isIndexedFilterSetEmpty(row.original.filters);
16036
+ return /*#__PURE__*/ jsxRuntime.jsxs(core$1.Group, {
15065
16037
  wrap: "nowrap",
15066
16038
  gap: "xs",
15067
16039
  children: [
15068
16040
  /*#__PURE__*/ jsxRuntime.jsx(core$1.Tooltip, {
15069
- label: "Submit Data Access Request",
16041
+ label: hasNoFilters ? 'No Filters defined. Cannot submit request.' : 'Submit Data Access Request',
15070
16042
  withArrow: true,
15071
16043
  children: /*#__PURE__*/ jsxRuntime.jsx(core$1.ActionIcon, {
15072
16044
  color: "accent.4",
15073
16045
  variant: "transparent",
16046
+ disabled: hasNoFilters,
15074
16047
  "aria-label": "Submit Data Access Request",
15075
16048
  onClick: ()=>{
15076
16049
  const modelId = modals.modals.open({
15077
16050
  title: 'DATA ACCESS REQUEST',
15078
16051
  children: /*#__PURE__*/ jsxRuntime.jsx(DataAccessRequestForm, {
15079
16052
  cohortId: row.id,
15080
- submitFunction: handleSubmission,
16053
+ submitFunction: handleCohortAccessRequest,
15081
16054
  close: ()=>modals.modals.close(modelId)
15082
16055
  })
15083
16056
  });
@@ -15117,16 +16090,21 @@ const SavedCohortsTable = ({ size = 'md' })=>{
15117
16090
  modals.modals.openConfirmModal({
15118
16091
  title: 'Delete Cohort',
15119
16092
  centered: true,
15120
- children: /*#__PURE__*/ jsxRuntime.jsx(core$1.Text, {
16093
+ children: /*#__PURE__*/ jsxRuntime.jsxs(core$1.Text, {
15121
16094
  size: "sm",
15122
- children: "Are you sure you want to delete your cohort? Deleted cohorts cannot be restored."
16095
+ children: [
16096
+ "Are you sure you want to delete your cohort? Deleted cohorts cannot be restored.",
16097
+ ' ',
16098
+ row.original?.requested ? `This cohort has an access request. Deleting this cohort will results in not being able to correlate nay pending requests.
16099
+ Are you sure you want to delete this cohort?.` : ''
16100
+ ]
15123
16101
  }),
15124
16102
  labels: {
15125
16103
  confirm: 'Delete Cohort',
15126
16104
  cancel: 'Cancel'
15127
16105
  },
15128
16106
  confirmProps: {
15129
- color: theme.colors.utility[2]
16107
+ color: theme.colors.accent[4]
15130
16108
  },
15131
16109
  onConfirm: ()=>{
15132
16110
  appDispatch(removeCohort(row.id));
@@ -15142,7 +16120,8 @@ const SavedCohortsTable = ({ size = 'md' })=>{
15142
16120
  })
15143
16121
  })
15144
16122
  ]
15145
- })
16123
+ });
16124
+ }
15146
16125
  });
15147
16126
  return /*#__PURE__*/ jsxRuntime.jsx(jsxRuntime.Fragment, {
15148
16127
  children: /*#__PURE__*/ jsxRuntime.jsx("div", {
@@ -15154,20 +16133,95 @@ const SavedCohortsTable = ({ size = 'md' })=>{
15154
16133
  });
15155
16134
  };
15156
16135
 
15157
- const SavedCohortsPanel = ()=>{
16136
+ const SavedCohortsPanel = ({ indexResources, remoteSupportService })=>{
15158
16137
  return /*#__PURE__*/ jsxRuntime.jsx(jsxRuntime.Fragment, {
15159
16138
  children: /*#__PURE__*/ jsxRuntime.jsx(core$1.Stack, {
15160
16139
  className: "w-100 m-2",
15161
16140
  children: /*#__PURE__*/ jsxRuntime.jsx(core$1.Group, {
15162
16141
  className: "w-100 bg-base-light p-4",
15163
- children: /*#__PURE__*/ jsxRuntime.jsx(SavedCohortsTable, {})
16142
+ children: /*#__PURE__*/ jsxRuntime.jsx(SavedCohortsTable, {
16143
+ indexResources: indexResources,
16144
+ remoteSupportService: remoteSupportService
16145
+ })
15164
16146
  })
15165
16147
  })
15166
16148
  });
15167
16149
  };
15168
16150
 
16151
+ const useGetCohortRequests = (resources = [])=>{
16152
+ const [isLoading, setIsLoading] = React.useState(false);
16153
+ const [cohortRequests, setCohortRequests] = React.useState([]);
16154
+ const localRequests = useAppSelector(selectAllDataAccessRequests);
16155
+ const { data: submittedRequests, isFetching, isError, error } = core.useUserRequestQuery({
16156
+ resource_ids: resources
16157
+ });
16158
+ const cohortIdToNameMap = useAppSelector(selectCohortIdToNameMap);
16159
+ React.useEffect(()=>{
16160
+ // Return early if we're still fetching data
16161
+ if (isFetching) {
16162
+ setIsLoading(true);
16163
+ return;
16164
+ }
16165
+ // Once data is available, merge local and remote requests
16166
+ if (submittedRequests) {
16167
+ // Create a map of local requests by ID
16168
+ const localRequestsMap = localRequests.reduce((acc, localRequest)=>{
16169
+ acc[localRequest.id] = localRequest;
16170
+ return acc;
16171
+ }, {});
16172
+ // Add cohort names to the requests
16173
+ const requestsWithCohorts = submittedRequests.reduce((acc, request)=>{
16174
+ if (request.request_id && Object.hasOwn(localRequestsMap, request.request_id)) {
16175
+ const localRequest = localRequestsMap[request.request_id];
16176
+ acc.push({
16177
+ ...localRequest,
16178
+ cohortName: cohortIdToNameMap[localRequest.cohortId] ?? 'Not Found',
16179
+ status: request?.status ?? 'unknown',
16180
+ updatedDatetime: request.created_time ?? localRequest.updatedDatetime,
16181
+ createdDatetime: request.updated_time ?? localRequest.createdDatetime
16182
+ });
16183
+ } else {
16184
+ acc.push({
16185
+ ...request,
16186
+ cohortId: 'Not Set',
16187
+ email: request.username ?? 'Not Set',
16188
+ name: 'Not Set',
16189
+ organization: 'Not Set',
16190
+ id: request.request_id ?? 'Not Set',
16191
+ cohortName: 'Not Found',
16192
+ status: request?.status ?? 'unknown',
16193
+ updatedDatetime: request.created_time ?? 'Not Set',
16194
+ createdDatetime: request.updated_time ?? ' Not Set'
16195
+ });
16196
+ }
16197
+ return acc;
16198
+ }, []);
16199
+ setCohortRequests(requestsWithCohorts);
16200
+ } else if (isError) {
16201
+ // If there's an error fetching remote data, use local requests
16202
+ const localRequestsWithCohorts = localRequests.map((request)=>({
16203
+ ...request,
16204
+ cohortName: request.id ? cohortIdToNameMap[request.id] : 'Unknown'
16205
+ }));
16206
+ setCohortRequests(localRequestsWithCohorts);
16207
+ }
16208
+ setIsLoading(false);
16209
+ }, [
16210
+ submittedRequests,
16211
+ localRequests,
16212
+ isFetching,
16213
+ isError,
16214
+ cohortIdToNameMap
16215
+ ]);
16216
+ return {
16217
+ cohortRequests,
16218
+ isLoading: isLoading || isFetching,
16219
+ isError,
16220
+ error
16221
+ };
16222
+ };
15169
16223
  const RequestsTable = ()=>{
15170
- const requests = useAppSelector(selectAllDataAccessRequests);
16224
+ const { cohortRequests, isLoading, isError } = useGetCohortRequests(undefined);
15171
16225
  const columns = React.useMemo(()=>[
15172
16226
  {
15173
16227
  accessorKey: 'cohortName',
@@ -15180,7 +16234,7 @@ const RequestsTable = ()=>{
15180
16234
  })
15181
16235
  },
15182
16236
  {
15183
- accessorKey: 'request_datetime',
16237
+ accessorKey: 'createdDatetime',
15184
16238
  header: 'Request Date',
15185
16239
  Cell: ({ cell })=>/*#__PURE__*/ jsxRuntime.jsxs(core$1.Text, {
15186
16240
  children: [
@@ -15189,6 +16243,16 @@ const RequestsTable = ()=>{
15189
16243
  ]
15190
16244
  })
15191
16245
  },
16246
+ {
16247
+ accessorKey: 'updatedDatetime',
16248
+ header: 'Last Update',
16249
+ Cell: ({ cell })=>/*#__PURE__*/ jsxRuntime.jsxs(core$1.Text, {
16250
+ children: [
16251
+ formatDate(cell.getValue()),
16252
+ " "
16253
+ ]
16254
+ })
16255
+ },
15192
16256
  {
15193
16257
  accessorKey: 'status',
15194
16258
  header: 'Status',
@@ -15205,21 +16269,26 @@ const RequestsTable = ()=>{
15205
16269
  }
15206
16270
  }
15207
16271
  ], []);
15208
- const cohortIdToNameMap = useAppSelector(selectCohortIdToNameMap);
15209
- const requestsWithCohorts = useDeepCompare.useDeepCompareMemo(()=>requests.map((request)=>{
15210
- return {
15211
- ...request,
15212
- cohortName: cohortIdToNameMap[request.cohortId] || 'Unknown'
15213
- };
15214
- }), [
15215
- requests,
15216
- cohortIdToNameMap
15217
- ]);
16272
+ const size = 'sm';
15218
16273
  const table = mantineReactTable.useMantineReactTable({
15219
16274
  columns,
15220
- data: requestsWithCohorts,
15221
- ...commonTableSettings$1()
16275
+ data: cohortRequests ?? [],
16276
+ renderEmptyRowsFallback: ()=>/*#__PURE__*/ jsxRuntime.jsx(EmptyTableMessage, {
16277
+ message: 'No requests submitted.'
16278
+ }),
16279
+ ...commonTableSettings$1(size),
16280
+ state: {
16281
+ isLoading: isLoading
16282
+ }
15222
16283
  });
16284
+ if (isError) {
16285
+ return /*#__PURE__*/ jsxRuntime.jsx("div", {
16286
+ className: "w-full mt-24 flex flex-col items-center justify-center",
16287
+ children: /*#__PURE__*/ jsxRuntime.jsx(ErrorCard, {
16288
+ message: `Error retrieving user request`
16289
+ })
16290
+ });
16291
+ }
15223
16292
  return /*#__PURE__*/ jsxRuntime.jsx("div", {
15224
16293
  className: "inline-block overflow-x-scroll w-full",
15225
16294
  children: /*#__PURE__*/ jsxRuntime.jsx(mantineReactTable.MantineReactTable, {
@@ -15229,16 +16298,18 @@ const RequestsTable = ()=>{
15229
16298
  };
15230
16299
 
15231
16300
  const RequestsPanel = ()=>{
15232
- return /*#__PURE__*/ jsxRuntime.jsx(core$1.Stack, {
15233
- className: "w-100 m-2",
15234
- children: /*#__PURE__*/ jsxRuntime.jsx(core$1.Group, {
15235
- className: "w-100 bg-base-light p-4",
15236
- children: /*#__PURE__*/ jsxRuntime.jsx(RequestsTable, {})
16301
+ return /*#__PURE__*/ jsxRuntime.jsx(ProtectedContent, {
16302
+ children: /*#__PURE__*/ jsxRuntime.jsx(core$1.Stack, {
16303
+ className: "w-100 m-2",
16304
+ children: /*#__PURE__*/ jsxRuntime.jsx(core$1.Group, {
16305
+ className: "w-100 bg-base-light p-4",
16306
+ children: /*#__PURE__*/ jsxRuntime.jsx(RequestsTable, {})
16307
+ })
15237
16308
  })
15238
16309
  });
15239
16310
  };
15240
16311
 
15241
- const TabbedIndex = ({ config, tabsLayout = 'left' })=>{
16312
+ const TabbedIndex = ({ config, indexResources, tabsLayout = 'left' })=>{
15242
16313
  return /*#__PURE__*/ jsxRuntime.jsx("div", {
15243
16314
  className: "w-full",
15244
16315
  children: /*#__PURE__*/ jsxRuntime.jsxs(core$1.Tabs, {
@@ -15261,9 +16332,12 @@ const TabbedIndex = ({ config, tabsLayout = 'left' })=>{
15261
16332
  children: /*#__PURE__*/ jsxRuntime.jsx(IndexPanel, {
15262
16333
  dataConfig: panelConfig.dataConfig,
15263
16334
  tabTitle: panelConfig.tabTitle,
16335
+ resourceField: panelConfig.resourceField,
15264
16336
  tabs: panelConfig.tabs,
15265
16337
  emptySelection: config.emptySelection,
15266
- numColumns: panelConfig.numColumns ?? 2
16338
+ numColumns: panelConfig.numColumns ?? 2,
16339
+ indexResources: indexResources,
16340
+ remoteSupportService: config.remoteSupportService
15267
16341
  })
15268
16342
  }, `${panelConfig.tabTitle}-tabPanel`))
15269
16343
  ]
@@ -15271,8 +16345,20 @@ const TabbedIndex = ({ config, tabsLayout = 'left' })=>{
15271
16345
  });
15272
16346
  };
15273
16347
 
16348
+ const extractIndexResourceFromConfiguration = (config)=>{
16349
+ return config.dataIndexes.reduce((acc, panelConfig)=>{
16350
+ acc[panelConfig.dataConfig.dataType] = {
16351
+ resourceField: panelConfig.resourceField,
16352
+ resourcePath: panelConfig.resourcePath
16353
+ };
16354
+ return acc;
16355
+ }, {});
16356
+ };
16357
+
15274
16358
  const persistor = reduxPersist.persistStore(AppStore);
15275
16359
  const CohortDiscovery = (config)=>{
16360
+ const indexResources = extractIndexResourceFromConfiguration(config);
16361
+ useAppDispatch(loadCohortsFromStorage());
15276
16362
  return /*#__PURE__*/ jsxRuntime.jsx(React.Fragment, {
15277
16363
  children: /*#__PURE__*/ jsxRuntime.jsxs(react$1.PersistGate, {
15278
16364
  persistor: persistor,
@@ -15286,7 +16372,7 @@ const CohortDiscovery = (config)=>{
15286
16372
  children: "Cohort Discovery"
15287
16373
  }),
15288
16374
  /*#__PURE__*/ jsxRuntime.jsxs(core$1.Tabs, {
15289
- keepMounted: true,
16375
+ keepMounted: false,
15290
16376
  defaultValue: "build",
15291
16377
  orientation: "vertical",
15292
16378
  classNames: {
@@ -15324,14 +16410,21 @@ const CohortDiscovery = (config)=>{
15324
16410
  tabTitle: config.dataIndexes[0].tabTitle,
15325
16411
  tabs: config.dataIndexes[0].tabs,
15326
16412
  emptySelection: config.emptySelection,
15327
- numColumns: config.dataIndexes[0].numColumns ?? 2
16413
+ resourceField: config.dataIndexes[0].resourceField,
16414
+ numColumns: config.dataIndexes[0].numColumns ?? 2,
16415
+ indexResources: indexResources,
16416
+ remoteSupportService: config.remoteSupportService
15328
16417
  }) : /*#__PURE__*/ jsxRuntime.jsx(TabbedIndex, {
15329
- config: config
16418
+ config: config,
16419
+ indexResources: indexResources
15330
16420
  })
15331
16421
  }),
15332
16422
  /*#__PURE__*/ jsxRuntime.jsx(core$1.Tabs.Panel, {
15333
16423
  value: "saved",
15334
- children: /*#__PURE__*/ jsxRuntime.jsx(SavedCohortsPanel, {})
16424
+ children: /*#__PURE__*/ jsxRuntime.jsx(SavedCohortsPanel, {
16425
+ indexResources: indexResources,
16426
+ remoteSupportService: config.remoteSupportService
16427
+ })
15335
16428
  }),
15336
16429
  /*#__PURE__*/ jsxRuntime.jsx(core$1.Tabs.Panel, {
15337
16430
  value: "request",
@@ -17808,7 +18901,8 @@ const DiscoveryPage = ({ headerProps, footerProps, discoveryConfig })=>{
17808
18901
  headerData: {
17809
18902
  title: 'Gen3 Discovery Page',
17810
18903
  content: 'Discovery Data',
17811
- key: 'gen3-discovery-page'
18904
+ key: 'gen3-discovery-page',
18905
+ ...discoveryConfig?.headerMetadata ? discoveryConfig.headerMetadata : {}
17812
18906
  },
17813
18907
  children: /*#__PURE__*/ jsxRuntime.jsx(Discovery, {
17814
18908
  discoveryConfig: discoveryConfig
@@ -17863,7 +18957,8 @@ const QueryPage = ({ headerProps, footerProps, queryProps })=>{
17863
18957
  headerData: {
17864
18958
  title: 'Gen3 Query Page',
17865
18959
  content: 'Query page',
17866
- key: 'gen3-query-page'
18960
+ key: 'gen3-query-page',
18961
+ ...queryProps?.headerMetadata ? queryProps.headerMetadata : {}
17867
18962
  },
17868
18963
  children: /*#__PURE__*/ jsxRuntime.jsx(QueryPanel, {
17869
18964
  graphQLEndpoint: queryProps.graphQLEndpoint
@@ -18035,7 +19130,8 @@ const LandingPage = ({ headerProps, footerProps, landingPage })=>{
18035
19130
  headerData: {
18036
19131
  title: 'Gen3 Home Page',
18037
19132
  content: 'Home page',
18038
- key: 'gen3-home-page'
19133
+ key: 'gen3-home-page',
19134
+ ...landingPage?.headerMetadata ? landingPage.headerMetadata : {}
18039
19135
  },
18040
19136
  children: /*#__PURE__*/ jsxRuntime.jsx("div", {
18041
19137
  className: "flex justify-items-center w-full",
@@ -18058,7 +19154,7 @@ const LandingPageGetServerSideProps = async ()=>{
18058
19154
  };
18059
19155
  };
18060
19156
 
18061
- const ExplorerPage = ({ headerProps, footerProps, explorerConfig, tabsLayout, sharedFiltersMap })=>{
19157
+ const ExplorerPage = ({ headerProps, footerProps, explorerConfig, headerMetadata, tabsLayout, sharedFiltersMap })=>{
18062
19158
  if (explorerConfig === undefined) {
18063
19159
  return /*#__PURE__*/ jsxRuntime.jsx(core$1.Center, {
18064
19160
  maw: 400,
@@ -18075,7 +19171,8 @@ const ExplorerPage = ({ headerProps, footerProps, explorerConfig, tabsLayout, sh
18075
19171
  headerData: {
18076
19172
  title: 'Gen3 Explorer Page',
18077
19173
  content: 'Explorer Page',
18078
- key: 'gen3-explorer-page'
19174
+ key: 'gen3-explorer-page',
19175
+ ...headerMetadata ? headerMetadata : {}
18079
19176
  },
18080
19177
  children: /*#__PURE__*/ jsxRuntime.jsx(CohortBuilder, {
18081
19178
  tabsLayout: tabsLayout,
@@ -18148,7 +19245,10 @@ const ExplorerPageGetServerSideProps = async ()=>{
18148
19245
  return {
18149
19246
  props: {
18150
19247
  ...await getNavPageLayoutPropsFromConfig(),
18151
- explorerConfig: cohortBuilderConfiguration
19248
+ explorerConfig: cohortBuilderConfiguration,
19249
+ headerMetadata: {
19250
+ ...cohortBuilderConfiguration?.headerMetadata
19251
+ }
18152
19252
  }
18153
19253
  };
18154
19254
  }
@@ -18159,6 +19259,7 @@ const ExplorerPageGetServerSideProps = async ()=>{
18159
19259
  sharedFiltersMap: sharedFiltersMap,
18160
19260
  tabsLayout: cohortBuilderConfiguration?.tabsLayout ?? 'left',
18161
19261
  explorerConfig: cohortBuilderConfiguration.explorerConfig,
19262
+ headerMetadata: cohortBuilderConfiguration.headerMetadata,
18162
19263
  accessControl: {
18163
19264
  ...DefaultAccessControlConfiguration,
18164
19265
  ...cohortBuilderConfiguration.accessControl ?? {}
@@ -18187,7 +19288,10 @@ const ExplorerPageGetServerSidePropsForConfigId = async (context)=>{
18187
19288
  return {
18188
19289
  props: {
18189
19290
  ...await getNavPageLayoutPropsFromConfig(),
18190
- explorerConfig: cohortBuilderConfiguration
19291
+ explorerConfig: cohortBuilderConfiguration,
19292
+ headerMetadata: {
19293
+ ...cohortBuilderConfiguration?.headerMetadata
19294
+ }
18191
19295
  }
18192
19296
  };
18193
19297
  }
@@ -18198,6 +19302,7 @@ const ExplorerPageGetServerSidePropsForConfigId = async (context)=>{
18198
19302
  sharedFiltersMap: sharedFiltersMap,
18199
19303
  tabsLayout: cohortBuilderConfiguration?.tabsLayout ?? 'left',
18200
19304
  explorerConfig: cohortBuilderConfiguration.explorerConfig,
19305
+ headerMetadata: cohortBuilderConfiguration.headerMetadata,
18201
19306
  accessControl: {
18202
19307
  ...DefaultAccessControlConfiguration,
18203
19308
  ...cohortBuilderConfiguration.accessControl ?? {}
@@ -19138,7 +20243,8 @@ const ProfilePage = ({ headerProps, footerProps, profileConfig })=>{
19138
20243
  headerData: {
19139
20244
  title: 'Gen3 Profile Page',
19140
20245
  content: 'Profile page',
19141
- key: 'gen3-profile-page'
20246
+ key: 'gen3-profile-page',
20247
+ ...profileConfig?.headerMetadata ? profileConfig.headerMetadata : {}
19142
20248
  },
19143
20249
  children: /*#__PURE__*/ jsxRuntime.jsx(Profile, {
19144
20250
  profileConfig: profileConfig
@@ -19165,7 +20271,8 @@ const LoginPage = ({ headerProps, footerProps, loginConfig })=>{
19165
20271
  headerData: {
19166
20272
  title: 'Gen3 Login Page',
19167
20273
  content: 'Login page',
19168
- key: 'gen3-login-page'
20274
+ key: 'gen3-login-page',
20275
+ ...loginConfig?.headerMetadata ? loginConfig.headerMetadata : {}
19169
20276
  },
19170
20277
  children: /*#__PURE__*/ jsxRuntime.jsx(LoginPanel, {
19171
20278
  ...loginConfig
@@ -19195,7 +20302,8 @@ const DictionaryPage = ({ headerProps, footerProps, config })=>{
19195
20302
  headerData: {
19196
20303
  title: 'Gen3 DataDictionary Page',
19197
20304
  content: 'Data Dictionary',
19198
- key: 'gen3-data-dictionary-page'
20305
+ key: 'gen3-data-dictionary-page',
20306
+ ...config?.headerMetadata ? config.headerMetadata : {}
19199
20307
  },
19200
20308
  children: /*#__PURE__*/ jsxRuntime.jsx(DictionaryWithContext, {
19201
20309
  config: config
@@ -20281,7 +21389,8 @@ const WorkspacePage = ({ headerProps, footerProps, workspaceProps })=>{
20281
21389
  headerData: {
20282
21390
  title: 'Gen3 Workspace Page',
20283
21391
  content: 'Workspace page',
20284
- key: 'gen3-workspace-page'
21392
+ key: 'gen3-workspace-page',
21393
+ ...workspaceProps?.headerMetadata ? workspaceProps.headerMetadata : {}
20285
21394
  },
20286
21395
  children: /*#__PURE__*/ jsxRuntime.jsx(Workspace, {
20287
21396
  config: workspaceProps
@@ -23446,7 +24555,8 @@ const CrosswalkPage = ({ headerProps, footerProps, config })=>{
23446
24555
  headerData: {
23447
24556
  title: 'Gen3 Crosswalk Page',
23448
24557
  content: 'Crosswalk Data',
23449
- key: 'gen3-crosswalk-page'
24558
+ key: 'gen3-crosswalk-page',
24559
+ ...config?.headerMetadata ? config.headerMetadata : {}
23450
24560
  },
23451
24561
  children: /*#__PURE__*/ jsxRuntime.jsx(CrosswalkPanel, {
23452
24562
  ...config
@@ -23936,6 +25046,7 @@ exports.analysisApiCohortDiscovery = analysisApiCohortDiscovery;
23936
25046
  exports.capitalize = capitalize;
23937
25047
  exports.computeRowSpan = computeRowSpan;
23938
25048
  exports.convertMinutesToMilliseconds = convertMinutesToMilliseconds;
25049
+ exports.convertPathsToTree = convertPathsToTree;
23939
25050
  exports.convertSecondsToMilliseconds = convertSecondsToMilliseconds;
23940
25051
  exports.create10ColorAccessibleContrast = create10ColorAccessibleContrast;
23941
25052
  exports.create10ColorPallet = create10ColorPallet;