@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/esm/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
2
  import * as React from 'react';
3
3
  import React__default, { createContext, useState, useMemo, useEffect, useCallback, useContext, useRef, createElement, forwardRef, useReducer } from 'react';
4
- import { useUserAuth, useCoreSelector, selectUserDetails, useGetAuthzMappingsQuery, fetchFence, isFetchError, WorkspaceStatus, useGetWorkspaceStatusQuery, useGetWorkspacePayModelsQuery, useTerminateWorkspaceMutation, selectRequestedWorkspaceStatus, selectRequestedWorkspaceStatusTimestamp, useCoreDispatch, setActiveWorkspaceStatus, RequestedWorkspaceStatus, setRequestedWorkspaceStatus, isTimeGreaterThan, useGetCSRFQuery, useLazyFetchUserDetailsQuery, selectUserAuthStatus, GEN3_REDIRECT_URL, GEN3_FENCE_API, showModal, Modals, isAuthenticated, useGetLoginProvidersQuery, hideModal, selectCurrentModal, fieldNameToTitle, useIsUserLoggedIn, GEN3_DOMAIN, useDataLibrary, DataLibraryStoreMode, buildListItemsGroupedByDataset, extractFileDatasetsInRecords, useGetMDSQuery, selectAuthzMappingData, useGetAggMDSQuery, useAskQuestionMutation, useGetIndexAggMDSQuery, useGetTagsQuery, isOperationWithField, extractEnumFilterValue, selectIndexedFilterByName as selectIndexedFilterByName$1, selectShouldShareFilters, selectSharedFilters, updateCohortFilter as updateCohortFilter$1, isOperatorWithFieldAndArrayOfOperands, removeCohortFilter as removeCohortFilter$1, setCohortFilter as setCohortFilter$1, clearCohortFilters as clearCohortFilters$1, handleOperation, selectAvailableCohorts, selectCurrentCohortId as selectCurrentCohortId$1, selectCohortFilters as selectCohortFilters$1, selectCurrentCohortModified, selectCurrentCohortSaved, setCohortIndexFilters, setActiveCohort, toggleCohortBuilderCategoryFilter, selectCohortFilterExpanded, setCohortFilterCombineMode, selectCohortFilterCombineMode, selectCurrentCohortName as selectCurrentCohortName$1, useGeneralGQLQuery, selectIndexFilters as selectIndexFilters$1, useGetRawDataAndTotalCountsQuery, isJSONValue, downloadFromGuppyToBlob, downloadJSONDataFromGuppy, Accessibility, trimFirstFieldNameToTitle, selectAllCohortFiltersCollapsed, toggleCohortBuilderAllFilters, setShouldShareFilters, useGetAggsQuery, useGetAggsNoFilterSelfQuery, isIntersection, useGetCountsQuery, setSharedFilters, selectHeadersWithCSRFToken, GEN3_GUPPY_API, useLaunchWorkspaceMutation, selectActiveWorkspaceStatus, isFetchBaseQueryError, setActiveWorkspace, getCurrentTimestamp, selectWorkspaceStatusFromService, selectActiveWorkspaceId, useGetWorkspaceOptionsQuery, GEN3_WORKSPACE_API, useGetExternalLoginsQuery, useSetCurrentPayModelMutation, PodStatus, PodConditionType, isWorkspaceRunningOrStopping, GEN3_COMMONS_NAME, createAppStore, buildGetAggregationQuery, processHistogramResponse, getGen3AppId, createGen3AppWithOwnStore, createAppApiForRTKQ, useGetDictionaryQuery, useGetMetadataByIdQuery, createGen3App, CoreProvider, fetchJSONDataFromURL, HttpMethod, groupSharedFields, useGetCredentialsQuery, useRemoveCredentialMutation, useAddNewCredentialMutation, useGetProjectsDetailsQuery, useGetSubmissionsQuery, isCohortItem, isFileItem, fetchFencePresignedURL, HTTPError, getFederatedLoginStatus, queryMultipleMDSRecords, GEN3_MANIFEST_API, extractIndexFromDataLibraryCohort, isFilterSet, getNumberOfItemsInDatalist, isAdditionalDataItem, useLazyGetCrosswalkDataQuery, roundHistogramResponse } from '@gen3/core';
5
- import { LoadingOverlay, Card, Group, Avatar, Text, Checkbox, Stack, Tooltip, UnstyledButton, ThemeIcon, Center, Box, Select, Button, Divider, Textarea, FileButton, Anchor, Modal, Grid, Table, ColorSwatch, useMantineTheme, Loader, Paper, Menu, Badge, Alert, Tabs, Accordion, Drawer, CopyButton, factory, useProps, getStyleObject, SegmentedControl, useCombobox, Combobox, TextInput, ScrollArea, Switch, Autocomplete, Title, ActionIcon, NumberInput, RangeSlider, MultiSelect, Transition, rem, Stepper, Image as Image$1, NavLink, Flex, Highlight, List, createTheme, Pagination, FileInput, CloseButton } from '@mantine/core';
4
+ import { useUserAuth, useCoreSelector, selectUserDetails, useGetAuthzMappingsQuery, fetchFence, isFetchError, WorkspaceStatus, useGetWorkspaceStatusQuery, useGetWorkspacePayModelsQuery, useTerminateWorkspaceMutation, selectRequestedWorkspaceStatus, selectRequestedWorkspaceStatusTimestamp, useCoreDispatch, setActiveWorkspaceStatus, RequestedWorkspaceStatus, setRequestedWorkspaceStatus, isTimeGreaterThan, useGetCSRFQuery, useLazyFetchUserDetailsQuery, selectUserAuthStatus, GEN3_REDIRECT_URL, GEN3_FENCE_API, showModal, Modals, isAuthenticated, useGetLoginProvidersQuery, hideModal, selectCurrentModal, fieldNameToTitle, useIsUserLoggedIn, GEN3_DOMAIN, useDataLibrary, DataLibraryStoreMode, buildListItemsGroupedByDataset, extractFileDatasetsInRecords, useGetMDSQuery, selectAuthzMappingData, useGetAggMDSQuery, useAskQuestionMutation, useGetIndexAggMDSQuery, useGetTagsQuery, isOperationWithField, extractEnumFilterValue, selectIndexedFilterByName, selectShouldShareFilters, selectSharedFilters, updateCohortFilter as updateCohortFilter$1, isOperatorWithFieldAndArrayOfOperands, removeCohortFilter as removeCohortFilter$1, setCohortFilter as setCohortFilter$1, clearCohortFilters as clearCohortFilters$1, handleOperation, selectAvailableCohorts, selectCurrentCohortId as selectCurrentCohortId$1, selectCohortFilters, selectCurrentCohortModified, selectCurrentCohortSaved, setCohortIndexFilters, setActiveCohort, toggleCohortBuilderCategoryFilter, selectCohortFilterExpanded, setCohortFilterCombineMode, selectCohortFilterCombineMode, selectCurrentCohortName as selectCurrentCohortName$1, useGeneralGQLQuery, selectIndexFilters, useGetRawDataAndTotalCountsQuery, isJSONValue, downloadFromGuppyToBlob, downloadJSONDataFromGuppy, Accessibility, trimFirstFieldNameToTitle, selectAllCohortFiltersCollapsed, toggleCohortBuilderAllFilters, setShouldShareFilters, useGetAggsQuery, useGetAggsNoFilterSelfQuery, isIntersection, useGetCountsQuery, setSharedFilters, selectHeadersWithCSRFToken, GEN3_GUPPY_API, useLaunchWorkspaceMutation, selectActiveWorkspaceStatus, isFetchBaseQueryError, setActiveWorkspace, getCurrentTimestamp, selectWorkspaceStatusFromService, selectActiveWorkspaceId, useGetWorkspaceOptionsQuery, GEN3_WORKSPACE_API, useGetExternalLoginsQuery, useSetCurrentPayModelMutation, PodStatus, PodConditionType, isWorkspaceRunningOrStopping, GEN3_COMMONS_NAME, getTimestamp, createAppStore, buildGetAggregationQuery, processHistogramResponse, getRemoteSupportServiceRegistry, isHttpStatusError, MissingServiceConfigurationError, useLazyGetAggsNoFilterSelfQuery, useCreateRequestMutation, useUserRequestQuery, getGen3AppId, createGen3AppWithOwnStore, createAppApiForRTKQ, useGetDictionaryQuery, useGetMetadataByIdQuery, createGen3App, CoreProvider, fetchJSONDataFromURL, HttpMethod, groupSharedFields, useGetCredentialsQuery, useRemoveCredentialMutation, useAddNewCredentialMutation, useGetProjectsDetailsQuery, useGetSubmissionsQuery, isCohortItem, isFileItem, fetchFencePresignedURL, HTTPError, getFederatedLoginStatus, queryMultipleMDSRecords, GEN3_MANIFEST_API, extractIndexFromDataLibraryCohort, isFilterSet, getNumberOfItemsInDatalist, isAdditionalDataItem, useLazyGetCrosswalkDataQuery, roundHistogramResponse } from '@gen3/core';
5
+ import { LoadingOverlay, Card, Group, Avatar, Text, Checkbox, Stack, Center, Loader, Tooltip, UnstyledButton, ThemeIcon, Box, Select, Button, Divider, Textarea, FileButton, Anchor, Modal, Grid, Table, ColorSwatch, useMantineTheme, Paper, Menu, Badge, Alert, Tabs, Accordion, Drawer, CopyButton, factory, useProps, getStyleObject, SegmentedControl, useCombobox, Combobox, TextInput, ScrollArea, Switch, Autocomplete, Title, ActionIcon, NumberInput, RangeSlider, MultiSelect, Transition, rem, Stepper, Image as Image$1, NavLink, Flex, Highlight, List, createTheme, Pagination, FileInput, CloseButton } from '@mantine/core';
6
6
  import { useDeepCompareEffect, useDeepCompareMemo, useDeepCompareCallback } from 'use-deep-compare';
7
7
  import { useRouter } from 'next/router';
8
8
  import { usePathname } from 'next/navigation';
9
- import { MdLogin, MdClose, MdKeyboardDoubleArrowLeft, MdUndo, MdSearch, MdInfo, MdExpandLess, MdExpandMore, MdFlip, MdAddCircle, MdRemoveCircle, MdOutlineArrowBack, MdOutlineArrowForward, MdKeyboardArrowDown, MdKeyboardArrowUp, MdDownload, MdLockOutline, MdGroup, MdOutlineBarChart, MdOutlineSearch, MdFormatQuote, MdDelete, MdAdd, MdOutlineRemoveCircle } from 'react-icons/md';
9
+ import { MdLogin, MdClose, MdKeyboardDoubleArrowLeft, MdUndo, MdSearch, MdInfo, MdExpandLess, MdExpandMore, MdFlip, MdAddCircle, MdRemoveCircle, MdOutlineArrowBack, MdOutlineArrowForward, MdKeyboardArrowDown, MdKeyboardArrowUp, MdDownload, MdLockOutline, MdAdd, MdGroup, MdOutlineBarChart, MdOutlineSearch, MdFormatQuote, MdDelete, MdOutlineRemoveCircle } from 'react-icons/md';
10
10
  import { getCookie, deleteCookie, setCookie } from 'cookies-next';
11
11
  import useSWR from 'swr';
12
12
  import { errors, importSPKI, jwtVerify, decodeJwt } from 'jose';
@@ -15,6 +15,7 @@ import tinycolor from 'tinycolor2';
15
15
  import { capitalize as capitalize$1, toString, isArray, get, omit, partial, cloneDeep, unset } from 'lodash';
16
16
  import { twMerge } from 'tailwind-merge';
17
17
  import { BsExclamationTriangle, BsArrowsFullscreen } from 'react-icons/bs';
18
+ import { IoMdInformationCircleOutline, IoMdArrowDropdown, IoMdRefresh, IoMdLogIn } from 'react-icons/io';
18
19
  import Markdown from 'react-markdown';
19
20
  import remarkGfm from 'remark-gfm';
20
21
  import { useCookies } from 'react-cookie';
@@ -43,7 +44,6 @@ import { LuCircleSlash, LuClock, LuLock, LuUnlock } from 'react-icons/lu';
43
44
  import tw from 'tailwind-styled-components';
44
45
  import 'universal-cookie';
45
46
  import { RiCloseCircleLine } from 'react-icons/ri';
46
- import { IoMdArrowDropdown, IoMdRefresh, IoMdLogIn, IoMdInformationCircleOutline } from 'react-icons/io';
47
47
  import { modals, ModalsProvider } from '@mantine/modals';
48
48
  import GraphiQL from 'graphiql';
49
49
  import fs from 'fs';
@@ -51,9 +51,10 @@ import path from 'path';
51
51
  import { PersistGate } from 'redux-persist/integration/react';
52
52
  import { persistReducer, persistStore } from 'redux-persist';
53
53
  import { combineReducers } from 'redux';
54
- import { createSlice, nanoid, createEntityAdapter, createSelector } from '@reduxjs/toolkit';
54
+ import { createSlice, nanoid, createEntityAdapter, createAsyncThunk, createSelector } from '@reduxjs/toolkit';
55
+ import { openDB } from 'idb';
55
56
  import createWebStorage from 'redux-persist/lib/storage/createWebStorage';
56
- import { useForm, matches, isEmail, isNotEmpty } from '@mantine/form';
57
+ import { useForm, isEmail, isNotEmpty } from '@mantine/form';
57
58
  import dynamic from 'next/dynamic';
58
59
  import YAML from 'yaml';
59
60
  import { filesize } from 'filesize';
@@ -531,6 +532,47 @@ const create10ColorAccessibleContrast = (pallet)=>{
531
532
  */ const convertSecondsToMilliseconds = (seconds)=>seconds * 1000;
532
533
  const MinutesToMilliseconds = (minutes)=>minutes * 60 * 1000;
533
534
 
535
+ const convertPathsToTree = (paths)=>{
536
+ const tree = {};
537
+ paths.forEach((path)=>{
538
+ const parts = path.split('/').filter((part)=>part !== '');
539
+ let current = tree;
540
+ // Build the path through the tree
541
+ for(let i = 0; i < parts.length; i++){
542
+ const part = parts[i];
543
+ const fullPath = parts.slice(0, i + 1).join('/');
544
+ if (!current[part]) {
545
+ current[part] = {
546
+ value: fullPath,
547
+ label: part,
548
+ children: {},
549
+ _hasChildren: false
550
+ };
551
+ }
552
+ // Mark parent as having children
553
+ if (i < parts.length - 1) {
554
+ current[part]._hasChildren = true;
555
+ }
556
+ current = current[part].children;
557
+ }
558
+ });
559
+ // Convert to final format
560
+ const convertToFinalFormat = (node)=>{
561
+ const result = {
562
+ value: node.value,
563
+ label: node.label
564
+ };
565
+ const childKeys = Object.keys(node.children);
566
+ if (childKeys.length > 0) {
567
+ result.children = childKeys.map((key)=>convertToFinalFormat(node.children[key]));
568
+ }
569
+ return result;
570
+ };
571
+ // Get the root nodes and convert them
572
+ const rootKeys = Object.keys(tree);
573
+ return rootKeys.map((key)=>convertToFinalFormat(tree[key]));
574
+ };
575
+
534
576
  const resourcePathFromProjectID = (projectID)=>{
535
577
  const split = projectID.split('-');
536
578
  const program = split[0];
@@ -875,7 +917,7 @@ const UPDATE_SESSION_LIMIT = MinutesToMilliseconds(5);
875
917
  */ const SessionProvider = ({ children, updateSessionTime = 5, inactiveTimeLimit = 20, workspaceInactivityTimeLimit = 0, logoutInactiveUsers = true, monitorWorkspace = true })=>{
876
918
  const router = useRouter();
877
919
  const coreDispatch = useCoreDispatch();
878
- useGetCSRFQuery();
920
+ const { isSuccess: isGetCSRFSuccess } = useGetCSRFQuery();
879
921
  useWorkspaceResourceMonitor(monitorWorkspace); // monitor workspaces if any are running or configured
880
922
  const [getUserDetails] = useLazyFetchUserDetailsQuery(); // Fetch user details
881
923
  const userStatus = useCoreSelector((state)=>selectUserAuthStatus(state));
@@ -962,10 +1004,14 @@ const UPDATE_SESSION_LIMIT = MinutesToMilliseconds(5);
962
1004
  updateSession,
963
1005
  endSession
964
1006
  ]);
965
- return /*#__PURE__*/ jsx(SessionContext.Provider, {
1007
+ if (isGetCSRFSuccess) return /*#__PURE__*/ jsx(SessionContext.Provider, {
966
1008
  value: value,
967
1009
  children: children
968
1010
  });
1011
+ else return /*#__PURE__*/ jsx(Center, {
1012
+ h: "100vh",
1013
+ children: /*#__PURE__*/ jsx(Loader, {})
1014
+ });
969
1015
  };
970
1016
 
971
1017
  const TooltipStyle = {
@@ -1096,6 +1142,28 @@ const ErrorCard = ({ message })=>/*#__PURE__*/ jsx(MessageCard, {
1096
1142
  color: "utility.3"
1097
1143
  });
1098
1144
 
1145
+ const EmptyTableMessage = ({ message })=>{
1146
+ return /*#__PURE__*/ jsxs(Group, {
1147
+ gap: "xs",
1148
+ justify: "center",
1149
+ classNames: {
1150
+ root: 'w-100 my-4'
1151
+ },
1152
+ children: [
1153
+ /*#__PURE__*/ jsx("span", {
1154
+ className: "text-utility-warning",
1155
+ children: /*#__PURE__*/ jsx(IoMdInformationCircleOutline, {
1156
+ size: "1.5rem"
1157
+ })
1158
+ }),
1159
+ /*#__PURE__*/ jsx(Text, {
1160
+ fw: 600,
1161
+ children: message
1162
+ })
1163
+ ]
1164
+ });
1165
+ };
1166
+
1099
1167
  const LoginProviderMultipleItems = ({ provider, handleLoginSelected })=>{
1100
1168
  const [value, setValue] = useState(null);
1101
1169
  return /*#__PURE__*/ jsxs("div", {
@@ -1530,6 +1598,7 @@ const filterRedirect = (redirect)=>{
1530
1598
  } else {
1531
1599
  redirectPath = redirect ?? '/';
1532
1600
  }
1601
+ console.log('redirectPath', redirectPath);
1533
1602
  return GEN3_REDIRECT_URL ? `${GEN3_REDIRECT_URL}/${redirectPath}` : redirectPath;
1534
1603
  };
1535
1604
  const LoginPanel = (loginConfig)=>{
@@ -2401,7 +2470,7 @@ const processChartData$2 = (facetData, valueType = 'count', total, maxBins = 100
2401
2470
  }));
2402
2471
  return results;
2403
2472
  };
2404
- const HorizontalBarChart = ({ data, valueType, total })=>{
2473
+ const HorizontalBarChart = ({ data, valueType, total, showLegendInChart = true })=>{
2405
2474
  const chartData = processChartData$2(data, valueType, total);
2406
2475
  const chartDefinition = useMemo(()=>{
2407
2476
  return {
@@ -2416,11 +2485,12 @@ const HorizontalBarChart = ({ data, valueType, total })=>{
2416
2485
  orient: 'vertical',
2417
2486
  left: '73%',
2418
2487
  type: 'scroll',
2419
- right: 15
2488
+ right: 15,
2489
+ show: showLegendInChart
2420
2490
  },
2421
2491
  grid: {
2422
2492
  left: '0%',
2423
- right: '30%',
2493
+ right: showLegendInChart ? '30%' : '3%',
2424
2494
  bottom: '25%',
2425
2495
  containLabel: true,
2426
2496
  height: '50%'
@@ -2545,7 +2615,8 @@ const Charts = ({ charts, data, counts, isSuccess, numCols = DEFAULT_COLS, style
2545
2615
  data: data === undefined ? [] : data,
2546
2616
  total: counts ?? 1,
2547
2617
  valueType: chart.valueType ?? 'count',
2548
- label: chart.label
2618
+ label: chart.label,
2619
+ showLegendInChart: chart.showLegendInChart
2549
2620
  })
2550
2621
  }, "modal-col-1"),
2551
2622
  /*#__PURE__*/ jsx(Grid.Col, {
@@ -2633,7 +2704,8 @@ const Charts = ({ charts, data, counts, isSuccess, numCols = DEFAULT_COLS, style
2633
2704
  data: data === undefined ? [] : data[field],
2634
2705
  total: counts ?? 1,
2635
2706
  valueType: charts[field].valueType ?? 'count',
2636
- label: charts[field].label
2707
+ label: charts[field].label,
2708
+ showLegendInChart: charts[field].showLegendInChart
2637
2709
  }),
2638
2710
  showLegends && /*#__PURE__*/ jsx(Card.Section, {
2639
2711
  inheritPadding: true,
@@ -3224,7 +3296,7 @@ const AccountButton = ({ leftIcon = undefined, rightIcon = undefined, iconSize =
3224
3296
  const mergedClassnames = mergeDefaultTailwindClassnames(classNamesDefaults, classNames);
3225
3297
  const router = useRouter();
3226
3298
  const handleSelected = async ()=>{
3227
- await router.push('Profile');
3299
+ await router.push('/Profile');
3228
3300
  };
3229
3301
  const userInfo = useCoreSelector((state)=>selectUserDetails(state));
3230
3302
  const userStatus = useCoreSelector((state)=>selectUserAuthStatus(state));
@@ -3242,7 +3314,7 @@ const AccountButton = ({ leftIcon = undefined, rightIcon = undefined, iconSize =
3242
3314
  };
3243
3315
 
3244
3316
  const handleSelected = async (isAuthenticated, router, referrer, endSession, externalLoginUrl)=>{
3245
- if (!isAuthenticated) await router.push(externalLoginUrl || `Login?redirect=${referrer}`);
3317
+ if (!isAuthenticated) await router.push(externalLoginUrl || `/Login?redirect=${referrer}`);
3246
3318
  else {
3247
3319
  if (endSession) endSession();
3248
3320
  }
@@ -7699,7 +7771,7 @@ const classifyFacets = (data, index, fieldMapping = [], facetDefinitionsFromConf
7699
7771
  };
7700
7772
  };
7701
7773
  const useGetFacetFilters$1 = (index, field)=>{
7702
- return useCoreSelector((state)=>selectIndexedFilterByName$1(state, index, field)) ?? {
7774
+ return useCoreSelector((state)=>selectIndexedFilterByName(state, index, field)) ?? {
7703
7775
  operator: 'and',
7704
7776
  operands: []
7705
7777
  };
@@ -8010,6 +8082,7 @@ flex items-start justify-between items-center flex-nowrap bg-secondary px-1.5 ro
8010
8082
  if (toggleSettings) toggleSettings();
8011
8083
  },
8012
8084
  "aria-label": "Search",
8085
+ className: header.iconStyle,
8013
8086
  children: /*#__PURE__*/ jsx(Icon, {
8014
8087
  width: 12,
8015
8088
  height: 12,
@@ -8142,7 +8215,7 @@ const DEFAULT_MINIMUM = 0;
8142
8215
  const DEFAULT_MAXIMUM = 100;
8143
8216
  const BAD_DATA_MESSAGE = 'No data for this field';
8144
8217
 
8145
- const FacetSelector = ({ category, facetName, hooks, width = 'w-full', description, fields, selectedFields, updateSelectedField, showSearch = true, header = {
8218
+ const FacetSelector = ({ category, facetName, hooks, width = 'w-full', description, fields, selectedFields, updateSelectedField, showSearch = true, showSettings = false, header = {
8146
8219
  Panel: FacetHeader,
8147
8220
  Label: FacetText,
8148
8221
  iconStyle: controlsIconStyle
@@ -8184,6 +8257,7 @@ const FacetSelector = ({ category, facetName, hooks, width = 'w-full', descripti
8184
8257
  hooks: hooks,
8185
8258
  facetName: facetName,
8186
8259
  showSearch: showSearch,
8260
+ showSettings: showSettings,
8187
8261
  toggleSearch: toggleSearch,
8188
8262
  header: header
8189
8263
  })
@@ -8849,7 +8923,7 @@ const CohortSelector = ({ showSelectedCohorts = true })=>{
8849
8923
  const coreDispatch = useCoreDispatch();
8850
8924
  const cohorts = useCoreSelector(selectAvailableCohorts);
8851
8925
  const currentCohortId = useCoreSelector(selectCurrentCohortId$1);
8852
- const filters = useCoreSelector(selectCohortFilters$1);
8926
+ const filters = useCoreSelector(selectCohortFilters);
8853
8927
  const currentCohortModified = useCoreSelector(selectCurrentCohortModified);
8854
8928
  const currentCohortSaved = useCoreSelector(selectCurrentCohortSaved);
8855
8929
  const cohortStatusMessage = currentCohortSaved ? 'Changes not saved' : 'Cohort not saved';
@@ -9011,7 +9085,7 @@ const reducer = (state, action)=>{
9011
9085
  }
9012
9086
  }
9013
9087
  };
9014
- const QueryExpressionSection = ({ index, showImportExport = false, displayOnly = false, showTitle = false })=>{
9088
+ const QueryExpressionSection = ({ index, showImportExport = false, displayOnly = false, showTitle = true })=>{
9015
9089
  const [expandedState, setExpandedState] = useReducer(reducer, {});
9016
9090
  const [filtersSectionCollapsed, setFiltersSectionCollapsed] = useState(true);
9017
9091
  const filtersRef = useRef(null);
@@ -9163,7 +9237,7 @@ const QueryExpressionSection = ({ index, showImportExport = false, displayOnly =
9163
9237
  };
9164
9238
 
9165
9239
  const useCohortFacetFilters$1 = (index)=>{
9166
- return useCoreSelector((state)=>selectCohortFilters$1(state)[index]);
9240
+ return useCoreSelector((state)=>selectCohortFilters(state)[index]);
9167
9241
  };
9168
9242
  const useToggleExpandFilter$1 = (index)=>{
9169
9243
  const dispatch = useCoreDispatch();
@@ -9893,7 +9967,7 @@ const DEFAULT_PAGE_LIMIT = 10000;
9893
9967
  const idField = detailsConfig?.idField;
9894
9968
  return (originalRow)=>idField && Object.keys(originalRow).includes(idField) ? originalRow[idField] : undefined;
9895
9969
  }, []);
9896
- const cohortFilters = useCoreSelector((state)=>selectIndexFilters$1(state, index));
9970
+ const cohortFilters = useCoreSelector((state)=>selectIndexFilters(state, index));
9897
9971
  const { data, isLoading, isError, isFetching } = useGetRawDataAndTotalCountsQuery({
9898
9972
  type: index,
9899
9973
  fields: fields,
@@ -11772,7 +11846,7 @@ const CohortPanel = ({ guppyConfig, filters, charts = {}, table, tabTitle, dropd
11772
11846
  ]);
11773
11847
  const [facetDefinitions, setFacetDefinitions] = useState({});
11774
11848
  const [summaryCharts, setSummaryCharts] = useState({});
11775
- const cohortFilters = useCoreSelector((state)=>selectIndexFilters$1(state, index));
11849
+ const cohortFilters = useCoreSelector((state)=>selectIndexFilters(state, index));
11776
11850
  const { data, isSuccess, isFetching: isAggsQueryFetching, isError: isAggsQueryError } = useGetAggsQuery({
11777
11851
  type: index,
11778
11852
  fields: fields,
@@ -12015,7 +12089,6 @@ const TabsLayoutToComponentProp = (tabsLayout)=>{
12015
12089
  };
12016
12090
 
12017
12091
  const CohortBuilder = ({ explorerConfig, sharedFiltersMap = null, tabsLayout = 'left' })=>{
12018
- useGetCSRFQuery();
12019
12092
  const dispatch = useCoreDispatch();
12020
12093
  dispatch(setSharedFilters(sharedFiltersMap ?? {}));
12021
12094
  return /*#__PURE__*/ jsx("div", {
@@ -13435,15 +13508,15 @@ const FacetSelectionPanel = ({ categories, hooks, selectedFields, updateSelected
13435
13508
  hooks
13436
13509
  ]);
13437
13510
  return /*#__PURE__*/ jsx("div", {
13438
- className: "w-1/3 flex flex-col grow-0 p-4 gap-y-6",
13511
+ 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",
13439
13512
  children: panels
13440
13513
  });
13441
13514
  };
13442
13515
 
13443
- const initialState$2 = {};
13516
+ const initialState$3 = {};
13444
13517
  const expandSlice = createSlice({
13445
13518
  name: 'CohortDiscovery/expandedCategory',
13446
- initialState: initialState$2,
13519
+ initialState: initialState$3,
13447
13520
  reducers: {
13448
13521
  toggleCategoryFilter: (state, action)=>{
13449
13522
  return {
@@ -13463,10 +13536,10 @@ const filtersExpandedReducer = expandSlice.reducer;
13463
13536
  const { toggleCategoryFilter, toggleAllFilters } = expandSlice.actions;
13464
13537
  const selectFilterExpanded = (state, field)=>state.filtersExpandedState?.[field];
13465
13538
 
13466
- const initialState$1 = {};
13539
+ const initialState$2 = {};
13467
13540
  const selectIndexFacets = createSlice({
13468
13541
  name: 'CohortDiscovery/selectedFacets',
13469
- initialState: initialState$1,
13542
+ initialState: initialState$2,
13470
13543
  reducers: {
13471
13544
  addFacetSelection: (state, action)=>{
13472
13545
  if (!(action.payload.index in state)) {
@@ -13501,12 +13574,12 @@ const selectedFacetsReducer = selectIndexFacets.reducer;
13501
13574
  const { addFacetSelection, removeFacetSelection } = selectIndexFacets.actions;
13502
13575
  const selectSelectedFacetsFromIndex = (state, index)=>state.selectedIndexFacets[index];
13503
13576
 
13504
- const initialState = {};
13577
+ const initialState$1 = {};
13505
13578
  /**
13506
13579
  * Redux slice for managing facet definitions in CohortDiscovery
13507
13580
  */ const facetDefinitionsSlice = createSlice({
13508
13581
  name: 'CohortDiscovery/facetDefinitions',
13509
- initialState,
13582
+ initialState: initialState$1,
13510
13583
  reducers: {
13511
13584
  addFacetDefinition: (state, action)=>{
13512
13585
  const { index, definition } = action.payload;
@@ -13536,311 +13609,955 @@ const initialState = {};
13536
13609
  const facetDefinitionsReducer = facetDefinitionsSlice.reducer;
13537
13610
  const { addFacetDefinition, addFacetDefinitions } = facetDefinitionsSlice.actions;
13538
13611
 
13539
- const initialCohortState = {
13540
- cohort: {
13541
- id: 'default',
13542
- name: 'Discovery',
13543
- filters: {},
13544
- modified_datetime: new Date().toISOString()
13545
- }
13546
- };
13547
- /**
13548
- * Redux slice for cohort filters
13549
- */ const cohortSlice = createSlice({
13550
- name: 'CohortDiscovery/cohorts',
13551
- initialState: initialCohortState,
13552
- reducers: {
13553
- // adds a filter to the cohort filter set at the given index
13554
- updateCohortFilter: (state, action)=>{
13555
- const { index, field, filter } = action.payload;
13556
- return {
13557
- cohort: {
13558
- ...state.cohort,
13559
- filters: {
13560
- ...state.cohort.filters,
13561
- [index]: {
13562
- mode: state.cohort.filters?.[index]?.mode ?? 'and',
13563
- root: {
13564
- ...state.cohort.filters?.[index]?.root ?? {},
13565
- [field]: filter
13566
- }
13567
- }
13568
- }
13569
- }
13570
- };
13571
- },
13572
- setCohortFilter: (state, action)=>{
13573
- const { index, filters } = action.payload;
13574
- return {
13575
- cohort: {
13576
- ...state.cohort,
13577
- filters: {
13578
- ...state.cohort.filters,
13579
- [index]: filters
13580
- }
13581
- }
13582
- };
13583
- },
13584
- // removes a filter to the cohort filter set at the given index
13585
- removeCohortFilter: (state, action)=>{
13586
- const { index, field } = action.payload;
13587
- const filters = state.cohort?.filters?.[index]?.root;
13588
- if (!filters) {
13589
- return;
13590
- }
13591
- const { [field]: _a, ...updated } = filters;
13592
- return {
13593
- cohort: {
13594
- ...state.cohort,
13595
- filters: {
13596
- ...state.cohort.filters,
13597
- [index]: {
13598
- mode: state.cohort.filters[index].mode,
13599
- root: updated
13600
- }
13601
- }
13602
- }
13603
- };
13604
- },
13605
- // removes all filters from the cohort filter set at the given index
13606
- clearCohortFilters: (state, action)=>{
13607
- const { index } = action.payload;
13608
- return {
13609
- cohort: {
13610
- ...state.cohort,
13611
- filters: {
13612
- ...state.cohort.filters,
13613
- [index]: {
13614
- // empty filter set
13615
- mode: 'and',
13616
- root: {}
13617
- }
13618
- }
13619
- }
13620
- };
13621
- }
13622
- }
13623
- });
13624
- // Filter actions: addFilter, removeFilter, updateFilter
13625
- const { updateCohortFilter, setCohortFilter, removeCohortFilter, clearCohortFilters } = cohortSlice.actions;
13626
- const cohortReducer = cohortSlice.reducer;
13627
-
13628
- /* eslint @typescript-eslint/no-unused-vars: 0 */ // Credit to: https://github.com/vercel/next.js/discussions/15687
13629
- const createNoopStorage = ()=>{
13630
- return {
13631
- getItem (_key) {
13632
- return Promise.resolve(null);
13633
- },
13634
- setItem (_key, value) {
13635
- return Promise.resolve(value);
13636
- },
13637
- removeItem (_key) {
13638
- return Promise.resolve();
13639
- }
13640
- };
13641
- };
13642
- const storage = typeof window !== 'undefined' ? createWebStorage('local') : createNoopStorage();
13643
- typeof window !== 'undefined' ? createWebStorage('session') : createNoopStorage();
13644
-
13645
13612
  const createCohortId = ()=>nanoid();
13646
- const createRequestId = ()=>nanoid();
13647
- const newCohort = (name, filters)=>{
13613
+ const newCohort = (name, filters, id, saved)=>{
13648
13614
  const ts = new Date().toISOString();
13649
13615
  return {
13650
- id: createCohortId(),
13616
+ id: id ?? createCohortId(),
13651
13617
  name: name,
13652
13618
  filters: filters,
13653
- modified_datetime: ts,
13654
- created_datetime: ts,
13619
+ modifiedDatetime: ts,
13620
+ createdDatetime: ts,
13655
13621
  requestedAccess: false,
13656
- requestId: 'no_request_id'
13622
+ requestId: 'no_request_id',
13623
+ saved: saved ?? false,
13624
+ modified: false
13657
13625
  };
13658
13626
  };
13659
- const newDataAccessRequest = (userInformation, cohortId)=>{
13627
+ const newDataAccessRequest = (requestId, status, userInformation, cohortId, createdTime, updatedTime)=>{
13628
+ const now = new Date().toISOString();
13660
13629
  return {
13661
- id: createRequestId(),
13662
- request_datetime: new Date().toISOString(),
13663
- status: 'pending',
13630
+ id: requestId,
13631
+ createdDatetime: createdTime ?? now,
13632
+ updatedDatetime: updatedTime ?? now,
13633
+ status: status,
13664
13634
  cohortId,
13665
13635
  ...userInformation
13666
13636
  };
13667
13637
  };
13638
+ const isIndexedFilterSetEmpty = (filters)=>Object.values(filters).every((filterSet)=>Object.keys(filterSet).length === 0);
13668
13639
 
13669
- // Create the entity adapter
13670
- const cohortsAdapter = createEntityAdapter({
13671
- // Select the id as the primary key
13672
- selectId: (cohort)=>cohort.id,
13673
- // Optional: Sort by name if needed
13674
- sortComparer: (a, b)=>a.name.localeCompare(b.name)
13675
- });
13676
- // Create a slice with reducers
13677
- const cohortPersistenceSlice = createSlice({
13678
- name: 'CohortDiscovery/CohortManager',
13679
- initialState: cohortsAdapter.getInitialState(),
13680
- reducers: {
13681
- // Add a single cohort
13682
- saveCohort: (state, action)=>{
13683
- const cohort = newCohort(action.payload.name, action.payload.filters);
13684
- cohortsAdapter.setOne(state, cohort);
13685
- },
13686
- // Remove a cohort
13687
- removeCohort: cohortsAdapter.removeOne,
13688
- // Remove multiple cohorts
13689
- removeCohorts: cohortsAdapter.removeMany
13640
+ const DATABASE_NAME = 'Gen3CohortDiscovery';
13641
+ const STORE_NAME = 'cohorts';
13642
+ const DB_SCHEMA_VERSION = 1;
13643
+ class CohortStorage {
13644
+ getDb() {
13645
+ try {
13646
+ return openDB(DATABASE_NAME, 1, {
13647
+ upgrade (db) {
13648
+ if (!db.objectStoreNames.contains(STORE_NAME)) {
13649
+ db.createObjectStore(STORE_NAME, {
13650
+ keyPath: 'id'
13651
+ });
13652
+ }
13653
+ }
13654
+ });
13655
+ } catch (error) {
13656
+ let errorMessage = 'Unknown error';
13657
+ if (error instanceof Error) {
13658
+ errorMessage = error.message;
13659
+ }
13660
+ throw new Error(`Database initialization failed: ${errorMessage}`);
13661
+ }
13690
13662
  }
13691
- });
13692
- // Export actions
13693
- const { saveCohort, removeCohort, removeCohorts } = cohortPersistenceSlice.actions;
13694
- // Export selectors
13695
- const { selectAll: selectAllCohorts, selectTotal: selectTotalCohorts, selectById: selectCohortById } = cohortsAdapter.getSelectors((state)=>state.savedCohorts);
13696
- const selectCohortIdToNameMap = createSelector([
13697
- selectAllCohorts
13698
- ], (cohorts)=>{
13699
- return cohorts.reduce((acc, cohort)=>{
13700
- acc[cohort.id] = cohort.name;
13701
- return acc;
13702
- }, {});
13703
- });
13704
- // Export reducer
13705
- const cohortPersistenceReducer = cohortPersistenceSlice.reducer;
13706
-
13707
- // Create the entity adapter
13708
- const dataAccessRequestsAdapter = createEntityAdapter({
13709
- // Select the id as the primary key
13710
- selectId: (request)=>request.id,
13711
- // Optional: Sort by request_datetime descending (newest first)
13712
- sortComparer: (a, b)=>new Date(b.request_datetime).getTime() - new Date(a.request_datetime).getTime()
13713
- });
13714
- // Create a slice with reducers
13715
- const dataAccessRequestsSlice = createSlice({
13716
- name: 'dataAccessRequests',
13717
- initialState: dataAccessRequestsAdapter.getInitialState(),
13718
- reducers: {
13719
- addDataAccessRequest: (state, action)=>{
13720
- const { cohortId, userAccessInformation } = action.payload;
13721
- const dataAccessRequests = newDataAccessRequest(userAccessInformation, cohortId);
13722
- dataAccessRequestsAdapter.addOne(state, dataAccessRequests);
13723
- },
13724
- // Set a request's status
13725
- setRequestStatus: (state, action)=>{
13726
- const { id, status } = action.payload;
13727
- dataAccessRequestsAdapter.updateOne(state, {
13728
- id,
13729
- changes: {
13730
- status
13663
+ /**
13664
+ * Initialize the IndexedDB database with proper schema
13665
+ */ async initializeDB() {
13666
+ try {
13667
+ return await openDB(DATABASE_NAME, DB_SCHEMA_VERSION, {
13668
+ upgrade: (db)=>{
13669
+ if (!db.objectStoreNames.contains(STORE_NAME)) {
13670
+ db.createObjectStore(STORE_NAME, {
13671
+ keyPath: 'id'
13672
+ });
13673
+ }
13674
+ },
13675
+ blocked: ()=>{
13676
+ console.warn('CohortDB: Database upgrade blocked by another connection');
13677
+ },
13678
+ blocking: ()=>{
13679
+ console.warn('CohortDB: This connection is blocking a database upgrade');
13680
+ },
13681
+ terminated: ()=>{
13682
+ console.error('CohortDB: Database connection terminated unexpectedly');
13731
13683
  }
13732
13684
  });
13685
+ } catch (error) {
13686
+ let errorMessage = 'Unknown error';
13687
+ if (error instanceof Error) {
13688
+ errorMessage = error.message;
13689
+ }
13690
+ throw new Error(`Database initialization failed: ${errorMessage}`);
13733
13691
  }
13734
13692
  }
13735
- });
13736
- // Export actions
13737
- const { addDataAccessRequest, setRequestStatus } = dataAccessRequestsSlice.actions;
13738
- // Export selectors
13739
- const { selectAll: selectAllDataAccessRequests, selectById: selectDataAccessRequestById, selectIds: selectDataAccessRequestIds, selectEntities: selectDataAccessRequestEntities, selectTotal: selectTotalDataAccessRequests } = dataAccessRequestsAdapter.getSelectors((state)=>state.dataAccessRequests);
13740
- const selectCohortToRequestId = createSelector([
13741
- selectAllDataAccessRequests
13742
- ], (requests)=>{
13743
- return requests.reduce((acc, request)=>{
13744
- acc[request.cohortId] = request.id;
13745
- return acc;
13746
- }, {});
13747
- });
13748
- // Export reducer
13749
- const dataAccessRequestsReducer = dataAccessRequestsSlice.reducer;
13750
-
13751
- const _APP_NAME$3 = 'CohortDiscovery';
13752
- const persistConfig = {
13753
- key: _APP_NAME$3,
13754
- version: 1,
13755
- storage
13756
- };
13757
- // create the store, context and selector for the ProjectsCenter
13758
- // Note the project app has a local store and context which isolates
13759
- // the filters and other store/cache values
13760
- const reducers = combineReducers({
13761
- selectedIndexFacets: selectedFacetsReducer,
13762
- filtersExpandedState: filtersExpandedReducer,
13763
- facetDefinitionState: facetDefinitionsReducer,
13764
- cohorts: cohortReducer,
13765
- savedCohorts: cohortPersistenceReducer,
13766
- dataAccessRequests: dataAccessRequestsReducer
13767
- });
13768
- const persistedReducers = persistReducer(persistConfig, reducers);
13769
- const { id, AppStore, AppContext, useAppSelector, useAppDispatch } = createAppStore({
13770
- reducers: persistedReducers,
13771
- name: _APP_NAME$3,
13772
- version: '0.0.1'
13773
- });
13774
-
13775
- const selectCohortFilters = (state)=>state.cohorts.cohort.filters;
13776
- const selectCurrentCohortId = (state)=>state.cohorts.cohort.id;
13777
- const selectCurrentCohortName = (state)=>state.cohorts.cohort.name;
13778
- /**
13779
- * Select a filter by its name from the current cohort. If the filter is not found
13780
- * returns undefined.
13781
- * @param state - Core
13782
- * @param index which cohort index to select from
13783
- * @param name name of the filter to select
13784
- */ const selectIndexedFilterByName = (state, index, name)=>{
13785
- return state.cohorts.cohort.filters[index]?.root[name];
13786
- };
13787
- const EmptyFilterSet = {
13788
- mode: 'and',
13789
- root: {}
13790
- };
13791
- /**
13792
- * Select a filter from the index.
13793
- * returns undefined.
13794
- * @param state - Core
13795
- * @param index which cohort index to select from
13796
- */ const selectIndexFilters = (state, index)=>{
13797
- return state.cohorts.cohort.filters?.[index] ?? EmptyFilterSet; // TODO: check if this is undefined
13798
- };
13799
-
13800
- const useToggleExpandFilter = ()=>{
13801
- const dispatch = useAppDispatch();
13802
- return (field, expanded)=>{
13803
- dispatch(toggleCategoryFilter({
13804
- field,
13805
- expanded
13806
- }));
13807
- };
13808
- };
13809
- const useFilterExpandedState = (field)=>{
13810
- return useAppSelector((state)=>selectFilterExpanded(state, field));
13811
- };
13812
- const useCohortFacetFilters = (index)=>{
13813
- return useAppSelector((state)=>selectCohortFilters(state)[index]);
13814
- };
13815
- const useClearFilters = (index)=>{
13816
- const dispatch = useAppDispatch();
13817
- return (field)=>{
13818
- dispatch(removeCohortFilter({
13819
- index,
13820
- field
13821
- }));
13822
- };
13823
- };
13824
- /**
13825
- * Update Guppy filters: process nested fields and have the final
13826
- * leaf be filtered
13827
- * @param index
13828
- */ const useUpdateFilters = (index)=>{
13829
- const dispatch = useAppDispatch();
13830
- // update the filter for this facet
13831
- return (field, filter)=>{
13832
- dispatch(updateCohortFilter({
13833
- index: index,
13834
- field: field,
13835
- filter: buildNested(field, filter)
13836
- }));
13837
- };
13693
+ // ===== CREATE OPERATIONS =====
13694
+ /**
13695
+ * Save a single cohort to the database
13696
+ */ async saveCohort(cohort) {
13697
+ try {
13698
+ const db = await this.getDb();
13699
+ const tx = db.transaction(STORE_NAME, 'readwrite');
13700
+ tx.objectStore(STORE_NAME).put(cohort);
13701
+ await tx.done;
13702
+ return {
13703
+ status: 200,
13704
+ message: 'cohort added'
13705
+ };
13706
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
13707
+ } catch (_error) {
13708
+ return {
13709
+ isError: true,
13710
+ status: 500,
13711
+ message: 'unable to save cohort'
13712
+ };
13713
+ }
13714
+ }
13715
+ /**
13716
+ * Save multiple cohorts in a single transaction (bulk operation)
13717
+ */ async saveCohorts(cohorts) {
13718
+ if (cohorts.length === 0) return {
13719
+ isError: true,
13720
+ status: 400,
13721
+ message: 'cannot add an empty array'
13722
+ };
13723
+ try {
13724
+ const db = await this.getDb();
13725
+ const tx = db.transaction(STORE_NAME, 'readwrite');
13726
+ // Batch all operations in single transaction for better performance
13727
+ await Promise.all([
13728
+ ...cohorts.map((cohort)=>tx.store.put({
13729
+ ...cohort,
13730
+ saved: true
13731
+ })),
13732
+ tx.done
13733
+ ]);
13734
+ return {
13735
+ status: 200,
13736
+ message: 'cohorts added'
13737
+ };
13738
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
13739
+ } catch (_error) {
13740
+ return {
13741
+ isError: true,
13742
+ status: 500,
13743
+ message: 'unable to save cohort'
13744
+ };
13745
+ }
13746
+ }
13747
+ // ===== READ OPERATIONS =====
13748
+ /**
13749
+ * Get a specific cohort by ID
13750
+ */ async getCohort(id) {
13751
+ try {
13752
+ const db = await this.getDb();
13753
+ const tx = db.transaction(STORE_NAME, 'readonly');
13754
+ const store = tx.objectStore(STORE_NAME);
13755
+ const cohort = await store.get(id);
13756
+ return {
13757
+ status: 200,
13758
+ message: 'success',
13759
+ data: cohort
13760
+ };
13761
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
13762
+ } catch (_error) {
13763
+ return {
13764
+ isError: true,
13765
+ status: 401,
13766
+ message: `cannot find cohort ${id}`
13767
+ };
13768
+ }
13769
+ }
13770
+ /**
13771
+ * Get all cohorts from the database
13772
+ */ async getAllCohorts() {
13773
+ try {
13774
+ const db = await this.getDb();
13775
+ const tx = db.transaction(STORE_NAME, 'readonly');
13776
+ const store = tx.objectStore(STORE_NAME);
13777
+ const savedCohorts = await store.getAll();
13778
+ if (!savedCohorts) {
13779
+ return {
13780
+ isError: true,
13781
+ status: 500,
13782
+ message: 'no cohorts returned'
13783
+ };
13784
+ }
13785
+ const cohorts = savedCohorts.reduce((acc, cohort)=>{
13786
+ const { id } = cohort;
13787
+ acc[id] = cohort;
13788
+ return acc;
13789
+ }, {});
13790
+ return {
13791
+ status: 200,
13792
+ message: 'success',
13793
+ data: cohorts
13794
+ };
13795
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
13796
+ } catch (_error) {
13797
+ return {
13798
+ isError: true,
13799
+ status: 401,
13800
+ message: 'cannot return cohorts'
13801
+ };
13802
+ }
13803
+ }
13804
+ /**
13805
+ * Search cohorts by name (case-insensitive partial match)
13806
+ */ async searchCohortsByName(searchTerm) {
13807
+ try {
13808
+ const db = await this.getDb();
13809
+ const tx = db.transaction(STORE_NAME, 'readonly');
13810
+ const store = tx.objectStore(STORE_NAME);
13811
+ const allCohorts = await store.getAll(STORE_NAME);
13812
+ // Filter in memory for partial name matching
13813
+ const searchLower = searchTerm.toLowerCase();
13814
+ return {
13815
+ status: 200,
13816
+ message: 'success',
13817
+ data: allCohorts.filter((cohort)=>cohort.name.toLowerCase().includes(searchLower)).reduce((acc, cohort)=>{
13818
+ const { id } = cohort;
13819
+ acc[id] = cohort;
13820
+ return acc;
13821
+ }, {})
13822
+ };
13823
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
13824
+ } catch (_error) {
13825
+ return {
13826
+ isError: true,
13827
+ status: 401,
13828
+ message: 'cannot find cohorts'
13829
+ };
13830
+ }
13831
+ }
13832
+ /**
13833
+ * Count total number of cohorts
13834
+ */ async getCohortCount() {
13835
+ try {
13836
+ const db = await this.getDb();
13837
+ const total = await db.count(STORE_NAME);
13838
+ return {
13839
+ status: 200,
13840
+ message: 'success',
13841
+ data: total
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 cohort count'
13849
+ };
13850
+ }
13851
+ }
13852
+ // ===== UPDATE OPERATIONS =====
13853
+ /**
13854
+ * Update an existing cohort (full replacement)
13855
+ */ async updateCohort(cohort) {
13856
+ try {
13857
+ const db = await this.getDb();
13858
+ // Verify cohort exists before updating
13859
+ const tx = db.transaction(STORE_NAME, 'readwrite');
13860
+ const store = tx.objectStore(STORE_NAME);
13861
+ const existing = await store.get(cohort.id);
13862
+ if (!existing) {
13863
+ return {
13864
+ isError: true,
13865
+ status: 401,
13866
+ message: 'cohort not found'
13867
+ };
13868
+ }
13869
+ const timestamp = getTimestamp();
13870
+ const updated = {
13871
+ ...existing,
13872
+ modifiedDatetime: timestamp
13873
+ };
13874
+ store.put(updated);
13875
+ await tx.done;
13876
+ return {
13877
+ status: 200,
13878
+ message: 'success'
13879
+ };
13880
+ } catch (error) {
13881
+ let errorMessage = 'An unknown error occurred';
13882
+ if (error instanceof Error) {
13883
+ errorMessage = error.message;
13884
+ }
13885
+ return {
13886
+ isError: true,
13887
+ status: 500,
13888
+ message: `Unable to update cohort: ${cohort.id}. Error: ${errorMessage}`
13889
+ };
13890
+ }
13891
+ }
13892
+ // ===== DELETE OPERATIONS =====
13893
+ /**
13894
+ * Delete a specific cohort by ID
13895
+ */ async deleteCohort(id) {
13896
+ try {
13897
+ const db = await this.getDb();
13898
+ const tx = db.transaction(STORE_NAME, 'readwrite');
13899
+ const store = tx.objectStore(STORE_NAME);
13900
+ // Verify cohort exists before deleting
13901
+ const existing = await db.get(STORE_NAME, id);
13902
+ if (!existing) {
13903
+ return {
13904
+ isError: true,
13905
+ status: 401,
13906
+ message: 'cohort not found'
13907
+ };
13908
+ }
13909
+ store.delete(id);
13910
+ await tx.done;
13911
+ return {
13912
+ status: 200,
13913
+ message: `${id} deleted`
13914
+ };
13915
+ } catch (error) {
13916
+ const errorMessage = error instanceof Error ? error.message : 'An unknown error occurred';
13917
+ return {
13918
+ isError: true,
13919
+ status: 500,
13920
+ message: `Unable to delete cohort: ${id}. Error: ${errorMessage}`
13921
+ };
13922
+ }
13923
+ }
13924
+ /**
13925
+ * Delete all cohorts from the database
13926
+ */ async deleteAllCohorts() {
13927
+ try {
13928
+ const db = await this.getDb();
13929
+ const tx = db.transaction(STORE_NAME, 'readwrite');
13930
+ const store = tx.objectStore(STORE_NAME);
13931
+ store.clear();
13932
+ await tx.done;
13933
+ return {
13934
+ status: 200,
13935
+ message: `all cohorts deleted`
13936
+ };
13937
+ } catch (error) {
13938
+ const errorMessage = error instanceof Error ? error.message : 'An unknown error occurred';
13939
+ return {
13940
+ isError: true,
13941
+ status: 500,
13942
+ message: `Unable to delete all cohorts. Error: ${errorMessage}`
13943
+ };
13944
+ }
13945
+ }
13946
+ // ===== UTILITY OPERATIONS =====
13947
+ /**
13948
+ * Check if a cohort exists
13949
+ */ async cohortExists(id) {
13950
+ try {
13951
+ const db = await this.getDb();
13952
+ const tx = db.transaction(STORE_NAME, 'readwrite');
13953
+ const store = tx.objectStore(STORE_NAME);
13954
+ // Verify cohort exists before deleting
13955
+ const existing = await db.get(STORE_NAME, id);
13956
+ return {
13957
+ status: 200,
13958
+ message: `${id}: ${existing ? 'true' : 'false'}`
13959
+ };
13960
+ } catch (error) {
13961
+ const errorMessage = error instanceof Error ? error.message : 'An unknown error occurred';
13962
+ return {
13963
+ isError: true,
13964
+ status: 500,
13965
+ message: `Unable search for cohort. Error: ${errorMessage}`
13966
+ };
13967
+ }
13968
+ }
13969
+ /**
13970
+ * Export all cohorts as JSON
13971
+ */ async exportCohorts() {
13972
+ return await this.getAllCohorts();
13973
+ }
13974
+ /**
13975
+ * Import cohorts from JSON data
13976
+ */ async importCohorts(cohorts, overwrite = false) {
13977
+ try {
13978
+ if (overwrite) {
13979
+ await this.deleteAllCohorts();
13980
+ }
13981
+ await this.saveCohorts(cohorts);
13982
+ } catch (error) {
13983
+ const errorMessage = error instanceof Error ? error.message : 'An unknown error occurred';
13984
+ return {
13985
+ isError: true,
13986
+ status: 500,
13987
+ message: `Failed to import cohorts: ${errorMessage}`
13988
+ };
13989
+ }
13990
+ }
13991
+ /**
13992
+ * Close the database connection
13993
+ */ async close() {
13994
+ try {
13995
+ const db = await this.getDb();
13996
+ db.close();
13997
+ } catch (error) {
13998
+ console.error('Failed to close database:', error);
13999
+ }
14000
+ }
14001
+ }
14002
+ // Export a singleton instance
14003
+ const cohortStorage = new CohortStorage();
14004
+
14005
+ const EmptyFilterSet = {
14006
+ mode: 'and',
14007
+ root: {}
14008
+ };
14009
+
14010
+ const isNameUnique = (state, name, excludeId)=>{
14011
+ const trimmedName = name.trim();
14012
+ if (!trimmedName) return false;
14013
+ return !Object.values(state.entities).some((cohort)=>cohort && cohort.id !== excludeId && cohort.name.trim().toLowerCase() === trimmedName.toLowerCase());
14014
+ };
14015
+ const generateUniqueName = (state, baseName)=>{
14016
+ const trimmedBaseName = baseName.trim();
14017
+ // If base name is unique, use it
14018
+ if (isNameUnique(state, trimmedBaseName)) {
14019
+ return trimmedBaseName;
14020
+ }
14021
+ // Find a unique name by appending numbers
14022
+ let counter = 1;
14023
+ let uniqueName;
14024
+ do {
14025
+ uniqueName = `${trimmedBaseName} (${counter})`;
14026
+ counter++;
14027
+ }while (!isNameUnique(state, uniqueName))
14028
+ return uniqueName;
14029
+ };
14030
+ const createDefaultCohort = ()=>newCohort(DEFAULT_COHORT_NAME, {}, undefined, false);
14031
+ // Entity adapter
14032
+ const cohortsAdapter = createEntityAdapter({
14033
+ selectId: (cohort)=>cohort.id,
14034
+ sortComparer: (a, b)=>{
14035
+ if (a.modifiedDatetime > b.modifiedDatetime) return -1;
14036
+ if (a.modifiedDatetime < b.modifiedDatetime) return 1;
14037
+ return 0;
14038
+ }
14039
+ });
14040
+ //const DEFAULT_COHORT_ID = 'default';
14041
+ const DEFAULT_COHORT_NAME = 'Default';
14042
+ const loadCohortsFromStorage = createAsyncThunk('cohorts/loadFromStorage', async ()=>{
14043
+ try {
14044
+ const cohortsMap = await cohortStorage.getAllCohorts();
14045
+ return Object(cohortsMap).values();
14046
+ } catch (error) {
14047
+ console.error('Failed to load cohorts:', error);
14048
+ return [];
14049
+ }
14050
+ });
14051
+ const saveCohortToStorage = createAsyncThunk('cohorts/saveToStorage', async (params, { getState })=>{
14052
+ const { cohortId, cohortName } = params;
14053
+ const state = getState();
14054
+ const cohort = state.cohorts.entities[cohortId];
14055
+ if (!cohort) {
14056
+ throw new Error(`Cohort with id ${cohortId} not found`);
14057
+ }
14058
+ const cohortToSave = {
14059
+ ...cohort,
14060
+ name: cohortName || cohort.name,
14061
+ saved: true,
14062
+ modified: false,
14063
+ modifiedDatetime: new Date().toISOString()
14064
+ };
14065
+ await cohortStorage.saveCohort(cohortToSave);
14066
+ return cohortToSave;
14067
+ });
14068
+ const autoSaveCohort = createAsyncThunk('cohorts/autoSave', async (cohortId, { getState })=>{
14069
+ const state = getState();
14070
+ const cohort = state.cohorts.entities[cohortId];
14071
+ if (!cohort || !cohort.saved) {
14072
+ throw new Error(`Cannot auto-save unsaved cohort ${cohortId}`);
14073
+ }
14074
+ const cohortToSave = {
14075
+ ...cohort,
14076
+ modified: false,
14077
+ modifiedDatetime: new Date().toISOString()
14078
+ };
14079
+ await cohortStorage.saveCohort(cohortToSave);
14080
+ return cohortToSave;
14081
+ });
14082
+ const deleteCohortFromStorage = createAsyncThunk('cohorts/deleteFromStorage', async (cohortId)=>{
14083
+ await cohortStorage.deleteCohort(cohortId);
14084
+ return cohortId;
14085
+ });
14086
+ const emptyInitialState = cohortsAdapter.getInitialState({
14087
+ currentCohortId: 'empty',
14088
+ loading: false,
14089
+ error: null,
14090
+ autoSaveInProgress: []
14091
+ });
14092
+ const initialState = cohortsAdapter.addOne(emptyInitialState, createDefaultCohort());
14093
+ const updateAutoSave = (cohortId, state)=>{
14094
+ // Add to auto-save queue if not already present
14095
+ if (!state.autoSaveInProgress.includes(cohortId)) {
14096
+ state.autoSaveInProgress.push(cohortId);
14097
+ }
14098
+ };
14099
+ const cohortManagerSlice = createSlice({
14100
+ name: 'CohortDiscovery/CohortManager',
14101
+ initialState,
14102
+ reducers: {
14103
+ setCurrentCohortId: (state, action)=>{
14104
+ if (state.entities[action.payload]) {
14105
+ state.currentCohortId = action.payload;
14106
+ }
14107
+ },
14108
+ createNewCohort: (state, action)=>{
14109
+ const baseName = action.payload.name || `New Cohort`;
14110
+ const uniqueName = generateUniqueName(state, baseName);
14111
+ const cohort = newCohort(uniqueName, action.payload.filters || {}, undefined, false);
14112
+ cohortsAdapter.addOne(state, cohort);
14113
+ state.currentCohortId = cohort.id;
14114
+ },
14115
+ removeCohort: (state, action)=>{
14116
+ const cohortId = action.payload;
14117
+ const totalCohorts = Object.keys(state.entities).length;
14118
+ if (totalCohorts <= 1) {
14119
+ cohortsAdapter.removeAll(state);
14120
+ const defaultCohort = createDefaultCohort();
14121
+ cohortsAdapter.addOne(state, defaultCohort);
14122
+ state.currentCohortId = defaultCohort.id;
14123
+ return;
14124
+ }
14125
+ cohortsAdapter.removeOne(state, cohortId);
14126
+ if (state.currentCohortId === cohortId) {
14127
+ const remainingIds = Object.keys(state.entities);
14128
+ state.currentCohortId = remainingIds[0];
14129
+ }
14130
+ state.autoSaveInProgress = state.autoSaveInProgress.filter((id)=>id !== cohortId);
14131
+ },
14132
+ updateCohortName: (state, action)=>{
14133
+ const { id, name } = action.payload;
14134
+ const cohort = state.entities[id];
14135
+ if (!cohort) return;
14136
+ const trimmedName = name.trim();
14137
+ // Don't update if name is empty
14138
+ if (!trimmedName) return;
14139
+ // Don't update if name hasn't changed
14140
+ if (cohort.name === trimmedName) return;
14141
+ // Check if name is unique (excluding current cohort)
14142
+ if (!isNameUnique(state, trimmedName, id)) {
14143
+ // Set error for duplicate name
14144
+ state.error = `A cohort named "${trimmedName}" already exists. Please choose a different name.`;
14145
+ return;
14146
+ }
14147
+ const wasSaved = cohort.saved;
14148
+ cohortsAdapter.updateOne(state, {
14149
+ id,
14150
+ changes: {
14151
+ name: trimmedName,
14152
+ modified: !wasSaved,
14153
+ modifiedDatetime: new Date().toISOString()
14154
+ }
14155
+ });
14156
+ if (wasSaved) {
14157
+ // Add to auto-save queue if not already present
14158
+ updateAutoSave(id, state);
14159
+ }
14160
+ },
14161
+ updateCohortFilter: (state, action)=>{
14162
+ const { index, field, filter } = action.payload;
14163
+ const currentCohort = getCurrentCohort$1(state);
14164
+ const wasSaved = currentCohort.saved;
14165
+ cohortsAdapter.updateOne(state, {
14166
+ id: currentCohort.id,
14167
+ changes: {
14168
+ filters: {
14169
+ ...currentCohort.filters,
14170
+ [index]: {
14171
+ mode: currentCohort.filters?.[index]?.mode ?? 'and',
14172
+ root: {
14173
+ ...currentCohort.filters?.[index]?.root ?? {},
14174
+ [field]: filter
14175
+ }
14176
+ }
14177
+ },
14178
+ modified: !wasSaved,
14179
+ modifiedDatetime: new Date().toISOString()
14180
+ }
14181
+ });
14182
+ if (wasSaved) {
14183
+ // Add to auto-save queue if not already present
14184
+ updateAutoSave(currentCohort.id, state);
14185
+ }
14186
+ },
14187
+ setCohortFilter: (state, action)=>{
14188
+ const { index, filters } = action.payload;
14189
+ const currentCohort = getCurrentCohort$1(state);
14190
+ const wasSaved = currentCohort.saved;
14191
+ cohortsAdapter.updateOne(state, {
14192
+ id: currentCohort.id,
14193
+ changes: {
14194
+ filters: {
14195
+ ...currentCohort.filters,
14196
+ [index]: filters
14197
+ },
14198
+ modified: !wasSaved,
14199
+ modifiedDatetime: new Date().toISOString()
14200
+ }
14201
+ });
14202
+ if (wasSaved) {
14203
+ updateAutoSave(currentCohort.id, state);
14204
+ }
14205
+ },
14206
+ setAllIndexFilters: (state, action)=>{
14207
+ const currentCohort = getCurrentCohort$1(state);
14208
+ const wasSaved = currentCohort.saved;
14209
+ cohortsAdapter.updateOne(state, {
14210
+ id: currentCohort.id,
14211
+ changes: {
14212
+ filters: action.payload,
14213
+ modified: !wasSaved,
14214
+ modifiedDatetime: new Date().toISOString()
14215
+ }
14216
+ });
14217
+ if (wasSaved) {
14218
+ updateAutoSave(currentCohort.id, state);
14219
+ }
14220
+ },
14221
+ removeCohortFilter: (state, action)=>{
14222
+ const { index, field } = action.payload;
14223
+ const currentCohort = getCurrentCohort$1(state);
14224
+ const filters = currentCohort.filters?.[index]?.root;
14225
+ const wasSaved = currentCohort.saved;
14226
+ if (!filters) return;
14227
+ const { [field]: _removed, ...updated } = filters;
14228
+ cohortsAdapter.updateOne(state, {
14229
+ id: currentCohort.id,
14230
+ changes: {
14231
+ filters: {
14232
+ ...currentCohort.filters,
14233
+ [index]: {
14234
+ mode: currentCohort.filters[index].mode,
14235
+ root: updated
14236
+ }
14237
+ },
14238
+ modified: !wasSaved,
14239
+ modifiedDatetime: new Date().toISOString()
14240
+ }
14241
+ });
14242
+ if (wasSaved) {
14243
+ updateAutoSave(currentCohort.id, state);
14244
+ }
14245
+ },
14246
+ clearCohortFilters: (state, action)=>{
14247
+ const { index } = action.payload;
14248
+ const currentCohort = getCurrentCohort$1(state);
14249
+ const wasSaved = currentCohort.saved;
14250
+ cohortsAdapter.updateOne(state, {
14251
+ id: currentCohort.id,
14252
+ changes: {
14253
+ filters: {
14254
+ ...currentCohort.filters,
14255
+ [index]: EmptyFilterSet
14256
+ },
14257
+ modified: !wasSaved,
14258
+ modifiedDatetime: new Date().toISOString()
14259
+ }
14260
+ });
14261
+ if (wasSaved) {
14262
+ updateAutoSave(currentCohort.id, state);
14263
+ }
14264
+ },
14265
+ clearError: (state)=>{
14266
+ state.error = null;
14267
+ }
14268
+ },
14269
+ extraReducers: (builder)=>{
14270
+ builder.addCase(loadCohortsFromStorage.pending, (state)=>{
14271
+ state.loading = true;
14272
+ state.error = null;
14273
+ }).addCase(loadCohortsFromStorage.fulfilled, (state, action)=>{
14274
+ state.loading = false;
14275
+ if (action.payload.length > 0) {
14276
+ if (Object.keys(state.entities).length === 1) {
14277
+ cohortsAdapter.removeOne(state, state.entities[0].id);
14278
+ }
14279
+ cohortsAdapter.addMany(state, action.payload);
14280
+ const sortedCohorts = action.payload.sort((a, b)=>b.modifiedDatetime.localeCompare(a.modifiedDatetime));
14281
+ state.currentCohortId = sortedCohorts[0].id;
14282
+ }
14283
+ }).addCase(loadCohortsFromStorage.rejected, (state, action)=>{
14284
+ state.loading = false;
14285
+ state.error = action.error.message || 'Failed to load cohorts';
14286
+ }).addCase(saveCohortToStorage.fulfilled, (state, action)=>{
14287
+ cohortsAdapter.updateOne(state, {
14288
+ id: action.payload.id,
14289
+ changes: action.payload
14290
+ });
14291
+ }).addCase(saveCohortToStorage.rejected, (state, action)=>{
14292
+ state.error = action.error.message || 'Failed to save cohort';
14293
+ }).addCase(autoSaveCohort.pending, (state, action)=>{
14294
+ updateAutoSave(action.meta.arg, state);
14295
+ }).addCase(autoSaveCohort.fulfilled, (state, action)=>{
14296
+ state.autoSaveInProgress = state.autoSaveInProgress.filter((id)=>id !== action.payload.id);
14297
+ cohortsAdapter.updateOne(state, {
14298
+ id: action.payload.id,
14299
+ changes: action.payload
14300
+ });
14301
+ }).addCase(autoSaveCohort.rejected, (state, action)=>{
14302
+ state.autoSaveInProgress = state.autoSaveInProgress.filter((id)=>id !== action.meta.arg);
14303
+ console.warn(`Auto-save failed for cohort ${action.meta.arg}:`, action.error.message);
14304
+ }).addCase(deleteCohortFromStorage.fulfilled, (state, action)=>{
14305
+ const cohortId = action.payload;
14306
+ const totalCohorts = Object.keys(state.entities).length;
14307
+ if (totalCohorts <= 1) {
14308
+ cohortsAdapter.removeAll(state);
14309
+ const defaultCohort = createDefaultCohort();
14310
+ cohortsAdapter.addOne(state, defaultCohort);
14311
+ state.currentCohortId = defaultCohort.id;
14312
+ } else {
14313
+ cohortsAdapter.removeOne(state, cohortId);
14314
+ if (state.currentCohortId === cohortId) {
14315
+ const remainingIds = Object.keys(state.entities);
14316
+ state.currentCohortId = remainingIds[0];
14317
+ }
14318
+ }
14319
+ state.autoSaveInProgress = state.autoSaveInProgress.filter((id)=>id !== cohortId);
14320
+ });
14321
+ }
14322
+ });
14323
+ // Helper function
14324
+ const getCurrentCohort$1 = (state)=>{
14325
+ const cohort = state.entities[state.currentCohortId];
14326
+ if (!cohort) {
14327
+ return createDefaultCohort();
14328
+ }
14329
+ return cohort;
14330
+ };
14331
+ // Actions export
14332
+ const { setCurrentCohortId, createNewCohort, removeCohort, updateCohortName, updateCohortFilter, setCohortFilter, setAllIndexFilters, removeCohortFilter, clearCohortFilters } = cohortManagerSlice.actions;
14333
+ const cohortManagerReducer = cohortManagerSlice.reducer;
14334
+
14335
+ const getCurrentCohort = (state)=>{
14336
+ const cohort = state.entities[state.currentCohortId];
14337
+ if (!cohort) {
14338
+ return createDefaultCohort();
14339
+ }
14340
+ return cohort;
14341
+ };
14342
+ const autoSaveMiddleware = (store)=>(next)=>(action)=>{
14343
+ const result = next(action);
14344
+ const autoSaveActions = [
14345
+ 'updateCohortName',
14346
+ 'updateCohortFilter',
14347
+ 'setCohortFilter',
14348
+ 'setAllIndexFilters',
14349
+ 'removeCohortFilter',
14350
+ 'clearCohortFilters'
14351
+ ];
14352
+ if (autoSaveActions.some((actionType)=>action.type.includes(actionType))) {
14353
+ const state = store.getState();
14354
+ const currentCohort = getCurrentCohort(state.cohorts);
14355
+ if (currentCohort.saved && !state.cohorts.autoSaveInProgress.has(currentCohort.id)) {
14356
+ setTimeout(()=>{
14357
+ const updatedState = store.getState();
14358
+ const updatedCohort = getCurrentCohort(updatedState.cohorts);
14359
+ if (updatedCohort.saved && !updatedState.cohorts.autoSaveInProgress.has(updatedCohort.id)) {
14360
+ store.dispatch(autoSaveCohort(updatedCohort.id));
14361
+ }
14362
+ }, 1000);
14363
+ }
14364
+ }
14365
+ return result;
14366
+ };
14367
+
14368
+ /* eslint @typescript-eslint/no-unused-vars: 0 */ // Credit to: https://github.com/vercel/next.js/discussions/15687
14369
+ const createNoopStorage = ()=>{
14370
+ return {
14371
+ getItem (_key) {
14372
+ return Promise.resolve(null);
14373
+ },
14374
+ setItem (_key, value) {
14375
+ return Promise.resolve(value);
14376
+ },
14377
+ removeItem (_key) {
14378
+ return Promise.resolve();
14379
+ }
14380
+ };
14381
+ };
14382
+ const storage = typeof window !== 'undefined' ? createWebStorage('local') : createNoopStorage();
14383
+ typeof window !== 'undefined' ? createWebStorage('session') : createNoopStorage();
14384
+
14385
+ // Create the entity adapter
14386
+ const dataAccessRequestsAdapter = createEntityAdapter({
14387
+ // Select the id as the primary key
14388
+ selectId: (request)=>request.id,
14389
+ // Optional: Sort by request_datetime descending (newest first)
14390
+ sortComparer: (a, b)=>new Date(b.createdDatetime).getTime() - new Date(a.createdDatetime).getTime()
14391
+ });
14392
+ // Create a slice with reducers
14393
+ const dataAccessRequestsSlice = createSlice({
14394
+ name: 'dataAccessRequests',
14395
+ initialState: dataAccessRequestsAdapter.getInitialState(),
14396
+ reducers: {
14397
+ addDataAccessRequest: (state, action)=>{
14398
+ const { cohortId, status, id, name, email, organization, createdDatetime, updatedDatetime } = action.payload;
14399
+ const dataAccessRequests = newDataAccessRequest(id, status, {
14400
+ name,
14401
+ email,
14402
+ organization
14403
+ }, cohortId, createdDatetime, updatedDatetime);
14404
+ dataAccessRequestsAdapter.addOne(state, dataAccessRequests);
14405
+ },
14406
+ // Set a request's status
14407
+ setRequestStatus: (state, action)=>{
14408
+ const { id, status } = action.payload;
14409
+ dataAccessRequestsAdapter.updateOne(state, {
14410
+ id,
14411
+ changes: {
14412
+ status
14413
+ }
14414
+ });
14415
+ }
14416
+ }
14417
+ });
14418
+ // Export actions
14419
+ const { addDataAccessRequest, setRequestStatus } = dataAccessRequestsSlice.actions;
14420
+ // Export selectors
14421
+ const { selectAll: selectAllDataAccessRequests, selectById: selectDataAccessRequestById, selectIds: selectDataAccessRequestIds, selectEntities: selectDataAccessRequestEntities, selectTotal: selectTotalDataAccessRequests } = dataAccessRequestsAdapter.getSelectors((state)=>state.dataAccessRequests);
14422
+ const selectCohortToRequestId = createSelector([
14423
+ selectAllDataAccessRequests
14424
+ ], (requests)=>{
14425
+ return requests.reduce((acc, request)=>{
14426
+ acc[request.cohortId] = request.id;
14427
+ return acc;
14428
+ }, {});
14429
+ });
14430
+ // Export reducer
14431
+ const dataAccessRequestsReducer = dataAccessRequestsSlice.reducer;
14432
+
14433
+ const _APP_NAME$3 = 'CohortDiscovery';
14434
+ const persistConfig = {
14435
+ key: _APP_NAME$3,
14436
+ version: 1,
14437
+ storage
14438
+ };
14439
+ // create the store, context and selector for the ProjectsCenter
14440
+ // Note the project app has a local store and context which isolates
14441
+ // the filters and other store/cache values
14442
+ const reducers = combineReducers({
14443
+ selectedIndexFacets: selectedFacetsReducer,
14444
+ filtersExpandedState: filtersExpandedReducer,
14445
+ facetDefinitionState: facetDefinitionsReducer,
14446
+ cohorts: cohortManagerReducer,
14447
+ // savedCohorts: saveCohortPersistenceReducer,
14448
+ dataAccessRequests: dataAccessRequestsReducer
14449
+ });
14450
+ const persistedReducers = persistReducer(persistConfig, reducers);
14451
+ const { id, AppStore, AppContext, useAppSelector, useAppDispatch } = createAppStore({
14452
+ reducers: persistedReducers,
14453
+ middleware: autoSaveMiddleware,
14454
+ name: _APP_NAME$3,
14455
+ version: '0.0.1'
14456
+ });
14457
+
14458
+ // Selectors
14459
+ const { selectAll: selectAllCohorts, selectTotal: selectTotalCohorts, selectById: selectCohortById, selectIds: selectCohortIds } = cohortsAdapter.getSelectors((state)=>state.cohorts);
14460
+ const selectCurrentCohortId = (state)=>state.cohorts.currentCohortId;
14461
+ const selectCurrentCohort = (state)=>{
14462
+ const { currentCohortId, entities } = state.cohorts;
14463
+ const cohort = entities[currentCohortId];
14464
+ if (!cohort) {
14465
+ return createDefaultCohort();
14466
+ }
14467
+ return cohort;
14468
+ };
14469
+ createSelector([
14470
+ selectAllCohorts
14471
+ ], (cohorts)=>cohorts.filter((cohort)=>cohort.saved));
14472
+ createSelector([
14473
+ selectAllCohorts
14474
+ ], (cohorts)=>cohorts.filter((cohort)=>!cohort.saved));
14475
+ createSelector([
14476
+ selectAllCohorts
14477
+ ], (cohorts)=>cohorts.filter((cohort)=>cohort.modified && !cohort.saved));
14478
+ createSelector([
14479
+ (state)=>Array.from(state.cohorts.autoSaveInProgress)
14480
+ ], (autoSaveArray)=>autoSaveArray);
14481
+ createSelector([
14482
+ (state)=>state.cohorts.autoSaveInProgress,
14483
+ (state, cohortId)=>cohortId
14484
+ ], (autoSaveArray, cohortId)=>autoSaveArray.includes(cohortId));
14485
+ const selectCurrentCohortName = createSelector([
14486
+ selectCurrentCohort
14487
+ ], (cohort)=>cohort.name);
14488
+ const selectCurrentCohortFilters = createSelector([
14489
+ selectCurrentCohort
14490
+ ], (cohort)=>cohort.filters);
14491
+ const selectCurrentCohortIndexFilters = createSelector([
14492
+ selectCurrentCohort,
14493
+ (state, index)=>index
14494
+ ], (cohort, index)=>{
14495
+ return cohort.filters?.[index] ?? EmptyFilterSet;
14496
+ });
14497
+ createSelector([
14498
+ selectAllCohorts,
14499
+ (state, name, excludeId)=>({
14500
+ name,
14501
+ excludeId
14502
+ })
14503
+ ], (cohorts, params)=>{
14504
+ const trimmedName = params.name.trim().toLowerCase();
14505
+ return cohorts.some((cohort)=>cohort.id !== params.excludeId && cohort.name.trim().toLowerCase() === trimmedName);
14506
+ });
14507
+ const selectCohortManagerLoading = (state)=>state.cohorts.loading;
14508
+ const selectCohortIdToNameMap = createSelector([
14509
+ selectAllCohorts
14510
+ ], (cohorts)=>{
14511
+ return cohorts.reduce((acc, cohort)=>{
14512
+ acc[cohort.id] = cohort.name;
14513
+ return acc;
14514
+ }, {});
14515
+ });
14516
+
14517
+ const useToggleExpandFilter = ()=>{
14518
+ const dispatch = useAppDispatch();
14519
+ return (field, expanded)=>{
14520
+ dispatch(toggleCategoryFilter({
14521
+ field,
14522
+ expanded
14523
+ }));
14524
+ };
14525
+ };
14526
+ const useFilterExpandedState = (field)=>{
14527
+ return useAppSelector((state)=>selectFilterExpanded(state, field));
14528
+ };
14529
+ const useCohortFacetFilters = (index)=>{
14530
+ return useAppSelector((state)=>selectCurrentCohortFilters(state)[index]);
14531
+ };
14532
+ const useClearFilters = (index)=>{
14533
+ const dispatch = useAppDispatch();
14534
+ return (field)=>{
14535
+ dispatch(removeCohortFilter({
14536
+ index,
14537
+ field
14538
+ }));
14539
+ };
14540
+ };
14541
+ /**
14542
+ * Update Guppy filters: process nested fields and have the final
14543
+ * leaf be filtered
14544
+ * @param index
14545
+ */ const useUpdateFilters = (index)=>{
14546
+ const dispatch = useAppDispatch();
14547
+ // update the filter for this facet
14548
+ return (field, filter)=>{
14549
+ dispatch(updateCohortFilter({
14550
+ index: index,
14551
+ field: field,
14552
+ filter: buildNested(field, filter)
14553
+ }));
14554
+ };
13838
14555
  };
13839
14556
  const useGetFacetFilters = (index, field)=>{
13840
- return useAppSelector((state)=>selectIndexedFilterByName(state, index, field) ?? {
13841
- operator: 'and',
13842
- operands: []
13843
- });
14557
+ return useAppSelector(selectCurrentCohortIndexFilters(index, field) ?? {
14558
+ operator: 'and',
14559
+ operands: []
14560
+ });
13844
14561
  };
13845
14562
 
13846
14563
  const EnumFacetPanel = ({ facet, hooks, valueLabel, showTotals = false, chartType = 'bar' })=>{
@@ -13859,12 +14576,12 @@ const EnumFacetPanel = ({ facet, hooks, valueLabel, showTotals = false, chartTyp
13859
14576
  label,
13860
14577
  field
13861
14578
  ]);
13862
- //const { data } = hooks.useGetFacetData(field);
13863
14579
  const chart = createChart(chartType, {
13864
14580
  data: visibleData,
13865
14581
  total: 1,
13866
14582
  valueType: 'count'
13867
14583
  });
14584
+ const clearFilters = hooks.useClearFilter();
13868
14585
  return /*#__PURE__*/ jsxs("div", {
13869
14586
  className: "flex flex-col bg-base-max relative border-base border-1 rounded-b-md transition",
13870
14587
  children: [
@@ -13876,9 +14593,26 @@ const EnumFacetPanel = ({ facet, hooks, valueLabel, showTotals = false, chartTyp
13876
14593
  fw: 600,
13877
14594
  children: facetName
13878
14595
  }),
13879
- showTotals ? /*#__PURE__*/ jsx(Text, {
13880
- children: "Todo"
13881
- }) : null
14596
+ /*#__PURE__*/ jsxs("div", {
14597
+ className: "flex nowrap",
14598
+ children: [
14599
+ showTotals ? /*#__PURE__*/ jsx(Text, {
14600
+ children: "Todo"
14601
+ }) : null,
14602
+ /*#__PURE__*/ jsx(Tooltip, {
14603
+ label: "Clear selection",
14604
+ children: /*#__PURE__*/ jsx(FacetIconButton, {
14605
+ onClick: ()=>clearFilters(field),
14606
+ "aria-label": "clear selection",
14607
+ color: "accent.4",
14608
+ children: /*#__PURE__*/ jsx(FaUndo, {
14609
+ size: "1.0em",
14610
+ "aria-hidden": "true"
14611
+ })
14612
+ })
14613
+ })
14614
+ ]
14615
+ })
13882
14616
  ]
13883
14617
  }),
13884
14618
  /*#__PURE__*/ jsx("div", {
@@ -14357,7 +15091,7 @@ const NUM_COLS = 2; // TODO: add to config
14357
15091
  * @param {Array} facets - The list of facets to be rendered.
14358
15092
  * @returns {JSX.Element} The rendered component showing facets.
14359
15093
  */ const ChartsAndFacetsPanel = ({ index, facets })=>{
14360
- const cohortFilters = useAppSelector((state)=>selectIndexFilters(state, index));
15094
+ const cohortFilters = useAppSelector((state)=>selectCurrentCohortIndexFilters(state, index));
14361
15095
  const { data, isSuccess} = useRoundedAggsQuery({
14362
15096
  type: index,
14363
15097
  fields: facets.map((x)=>x.field),
@@ -14429,105 +15163,483 @@ const NUM_COLS = 2; // TODO: add to config
14429
15163
  });
14430
15164
  };
14431
15165
 
14432
- const SaveActionButton = ({ index })=>{
14433
- const [opened, { close, open }] = useDisclosure(false);
14434
- const filters = useAppSelector((state)=>selectCohortFilters(state));
14435
- const [cohortName, setCohortName] = useState('New Cohort');
14436
- const appDispatch = useAppDispatch();
14437
- return /*#__PURE__*/ jsxs(Fragment, {
15166
+ const IconSize = {
15167
+ xs: 14,
15168
+ sm: 16,
15169
+ md: 20,
15170
+ lg: 24,
15171
+ xl: 32,
15172
+ 'xl-2': 36
15173
+ };
15174
+
15175
+ const DataAccessRequestForm = ({ cohortId, submitFunction, close })=>{
15176
+ const [submitted, setSubmitted] = useState(false);
15177
+ const form = useForm({
15178
+ initialValues: {
15179
+ name: '',
15180
+ organization: '',
15181
+ email: ''
15182
+ },
15183
+ validate: {
15184
+ name: isNotEmpty('Name is required'),
15185
+ email: isEmail('Please provide a valid email')
15186
+ }
15187
+ });
15188
+ const handleSubmit = async (values)=>{
15189
+ submitFunction(cohortId, values);
15190
+ setSubmitted(true);
15191
+ };
15192
+ if (submitted) {
15193
+ return /*#__PURE__*/ jsxs(Paper, {
15194
+ shadow: "md",
15195
+ p: "xl",
15196
+ radius: "md",
15197
+ children: [
15198
+ /*#__PURE__*/ jsx(Title, {
15199
+ order: 2,
15200
+ mb: "md",
15201
+ ta: "center",
15202
+ children: "Request Submitted"
15203
+ }),
15204
+ /*#__PURE__*/ jsx(Text, {
15205
+ ta: "center",
15206
+ children: "Thank you for your data access request. We will review your submission and contact you shortly."
15207
+ }),
15208
+ /*#__PURE__*/ jsx(Group, {
15209
+ justify: "center",
15210
+ mt: "xl",
15211
+ children: /*#__PURE__*/ jsx(Button, {
15212
+ onClick: ()=>close(),
15213
+ children: "Dismiss"
15214
+ })
15215
+ })
15216
+ ]
15217
+ });
15218
+ }
15219
+ return /*#__PURE__*/ jsxs(Paper, {
15220
+ radius: "md",
14438
15221
  children: [
14439
- /*#__PURE__*/ jsx(Modal, {
14440
- opened: opened,
14441
- onClose: close,
14442
- radius: "md",
14443
- title: "Save Cohort",
14444
- classNames: {
14445
- header: 'p-1 px-4 border-base-lighter border-b-1 uppercase font-bold'
14446
- },
15222
+ /*#__PURE__*/ jsx(Text, {
15223
+ c: "dimmed",
15224
+ mb: "lg",
15225
+ children: "Please complete all fields to request access to our research data."
15226
+ }),
15227
+ /*#__PURE__*/ jsx(Divider, {
15228
+ mb: "lg"
15229
+ }),
15230
+ /*#__PURE__*/ jsx(Box, {
15231
+ component: "form",
15232
+ onSubmit: form.onSubmit(handleSubmit),
14447
15233
  children: /*#__PURE__*/ jsxs(Stack, {
14448
- className: "mt-2",
15234
+ gap: "md",
14449
15235
  children: [
14450
15236
  /*#__PURE__*/ jsx(TextInput, {
14451
- description: "Enter name to save cohort",
14452
- label: "Cohort Name",
14453
- placeholder: "New Cohort",
14454
- "data-autofocus": true,
14455
- onChange: (event)=>{
14456
- setCohortName(event.currentTarget.value);
14457
- },
14458
- value: cohortName,
14459
- required: true
15237
+ label: "Full Name",
15238
+ placeholder: "John Doe",
15239
+ withAsterisk: true,
15240
+ ...form.getInputProps('name')
15241
+ }),
15242
+ /*#__PURE__*/ jsx(TextInput, {
15243
+ label: "Organization",
15244
+ placeholder: "University, Organization, or Company",
15245
+ withAsterisk: true,
15246
+ ...form.getInputProps('organization')
15247
+ }),
15248
+ /*#__PURE__*/ jsx(TextInput, {
15249
+ label: "Email",
15250
+ placeholder: "your.email@institution.edu",
15251
+ withAsterisk: true,
15252
+ ...form.getInputProps('email')
14460
15253
  }),
14461
15254
  /*#__PURE__*/ jsxs(Group, {
14462
15255
  justify: "flex-end",
14463
- className: "bg-base-lighter -m-4 p-3 mt-3",
14464
15256
  children: [
14465
15257
  /*#__PURE__*/ jsx(Button, {
15258
+ type: "reset",
14466
15259
  variant: "outline",
14467
- onClick: close,
14468
- children: "Cancel"
15260
+ onClick: ()=>form.reset(),
15261
+ children: "Reset"
14469
15262
  }),
14470
15263
  /*#__PURE__*/ jsx(Button, {
14471
- onClick: ()=>{
14472
- appDispatch(saveCohort({
14473
- name: cohortName,
14474
- filters: filters
14475
- }));
14476
- close();
14477
- },
14478
- children: "Save"
15264
+ variant: "outline",
15265
+ onClick: ()=>close(),
15266
+ children: submitted ? 'Close' : 'Cancel'
15267
+ }),
15268
+ !submitted && /*#__PURE__*/ jsx(Button, {
15269
+ type: "submit",
15270
+ children: "Submit Request"
14479
15271
  })
14480
15272
  ]
14481
15273
  })
14482
15274
  ]
14483
15275
  })
15276
+ })
15277
+ ]
15278
+ });
15279
+ };
15280
+
15281
+ class QueryAllIndexedError extends Error {
15282
+ constructor(message){
15283
+ super(message);
15284
+ this.name = 'QueryAllIndexedError';
15285
+ }
15286
+ }
15287
+ /**
15288
+ * Queries all resources based on the provided filters and resource fields, executing an aggregation query for each resource type.
15289
+ *
15290
+ * @async
15291
+ * @function
15292
+ * @param {IndexedFilterSet} filters - A set of filters indexed by resource types, used to refine the query results for each resource type.
15293
+ * @param {IndexResourceField} resourceFields - A mapping of resource types to their respective field configurations, including resource paths and field details.
15294
+ * @param {TriggerFunctionType} triggerAggsQuery - A function that triggers the aggregation query for a specific resource type and its associated fields.
15295
+ * @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".
15296
+ * @returns {Promise<string[]>} A promise that resolves to an array of resource paths for all queried and processed resources.
15297
+ * @throws {QueryAllIndexedError} Throws an error if the query operation fails or an unknown error occurs during execution.
15298
+ */ const queryAllResources = async (filters, resourceFields, triggerAggsQuery, excludeList = [
15299
+ '',
15300
+ 'no data'
15301
+ ])=>{
15302
+ try {
15303
+ const promises = Object.entries(resourceFields).map(([index, fieldConfig])=>triggerAggsQuery({
15304
+ filters: filters[index],
15305
+ type: index,
15306
+ fields: [
15307
+ fieldConfig.resourceField
15308
+ ],
15309
+ accessibility: Accessibility.ALL
15310
+ }).unwrap().then((result)=>({
15311
+ index,
15312
+ result
15313
+ })));
15314
+ const resources = await Promise.all(promises);
15315
+ const resultsByIndex = resources.reduce((acc, resource)=>{
15316
+ const filteredResults = Object.values(resource.result).map((hist)=>hist.map((h)=>h.key)).flat().filter((x)=>typeof x === 'string' && !excludeList.includes(x));
15317
+ const uniqueResults = new Set(filteredResults);
15318
+ const stringResults = Array.from(uniqueResults);
15319
+ acc[resource.index] = stringResults;
15320
+ return acc;
15321
+ }, {});
15322
+ return Object.entries(resultsByIndex).reduce((acc, [index, results])=>{
15323
+ const resourcePath = resourceFields[index].resourcePath;
15324
+ if (resourcePath) {
15325
+ acc.push(...results.map((x)=>`${resourcePath}/${x}`));
15326
+ } else {
15327
+ acc.push(...results.map((x)=>`/${x}`));
15328
+ }
15329
+ return acc;
15330
+ }, []);
15331
+ } catch (error) {
15332
+ if (error instanceof Error) {
15333
+ throw new QueryAllIndexedError(error.message);
15334
+ } else {
15335
+ throw new QueryAllIndexedError('Unknown error');
15336
+ }
15337
+ }
15338
+ };
15339
+
15340
+ const REQUESTOR_HTTP_ERROR_MESSAGES = {
15341
+ 401: 'You are not authorized to request access to this data. Please contact the site administrator.',
15342
+ 403: 'You do not have the correct permissions to request this resource. Please contact the site administrator.'
15343
+ };
15344
+ const submitCohortRequestAction = async (cohort, values, user, indexResources, remoteSupportService, appDispatch, getAggs, createRequestQuery)=>{
15345
+ if (cohort && values) {
15346
+ // need to get the resources requires by the cohort
15347
+ try {
15348
+ // query for resources from
15349
+ const resources = await queryAllResources(cohort.filters, indexResources, getAggs);
15350
+ // now we have the resources, submit a requestor request
15351
+ const request = await createRequestQuery({
15352
+ resource_paths: resources
15353
+ }).unwrap();
15354
+ // followed by a zendesk request
15355
+ const zendeskRequestAction = getRemoteSupportServiceRegistry().getSupportService(remoteSupportService.service);
15356
+ await zendeskRequestAction({
15357
+ subject: `Request for access to ${cohort.name}`,
15358
+ fullName: `${values.name}`,
15359
+ email: `${values.email}`,
15360
+ 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}`
15361
+ }, remoteSupportService.configuration);
15362
+ // update the request store
15363
+ appDispatch(addDataAccessRequest({
15364
+ cohortId: cohort.id,
15365
+ id: request.request_id ?? 'unknown',
15366
+ name: values.name,
15367
+ email: values.email,
15368
+ status: request.status ?? 'unknown',
15369
+ organization: values.organization,
15370
+ createdDatetime: request.created_time ?? 'unknown',
15371
+ updatedDatetime: request.updated_time ?? 'unknown'
15372
+ }));
15373
+ // Close the modal after a successful dispatch
15374
+ close();
15375
+ } catch (error) {
15376
+ if (error instanceof QueryAllIndexedError) {
15377
+ notifications.show({
15378
+ title: 'Request Cohort',
15379
+ message: `Error while getting resources for cohort: ${error.message}`
15380
+ });
15381
+ } else if (isHttpStatusError(error)) {
15382
+ const httpError = error;
15383
+ notifications.show({
15384
+ title: 'Request Cohort',
15385
+ message: REQUESTOR_HTTP_ERROR_MESSAGES[httpError.status] || `Error while submitting resource request for cohorts`
15386
+ });
15387
+ } else if (error instanceof MissingServiceConfigurationError) {
15388
+ notifications.show({
15389
+ title: 'Request Cohort',
15390
+ message: `Error while submitting resource request for cohorts: ${error.message}`
15391
+ });
15392
+ } else if (error instanceof Error) {
15393
+ notifications.show({
15394
+ title: 'Request Cohort',
15395
+ message: `Error while submitting resource request for cohorts: ${error.message}`
15396
+ });
15397
+ } else {
15398
+ notifications.show({
15399
+ title: 'Request Cohort',
15400
+ message: 'Unknown error while submitting resource request for cohorts'
15401
+ });
15402
+ }
15403
+ }
15404
+ }
15405
+ };
15406
+
15407
+ const CohortManager = ({ indexResources, remoteSupportService, size = 'md' })=>{
15408
+ const dispatch = useAppDispatch();
15409
+ const [isEditing, setIsEditing] = useState(false);
15410
+ const [editingLabel, setEditingLabel] = useState('');
15411
+ const allCohorts = useAppSelector(selectAllCohorts);
15412
+ const currentCohort = useAppSelector(selectCurrentCohort);
15413
+ const loading = useAppSelector(selectCohortManagerLoading);
15414
+ const appDispatch = useAppDispatch();
15415
+ const [getGetAggs] = useLazyGetAggsNoFilterSelfQuery();
15416
+ const [requestQuery] = useCreateRequestMutation();
15417
+ const user = useCoreSelector(selectUserDetails);
15418
+ const theme = useMantineTheme();
15419
+ const iconSize = IconSize[size] || IconSize['sm'];
15420
+ const startEditing = ()=>{
15421
+ setIsEditing(true);
15422
+ setEditingLabel(currentCohort.name);
15423
+ };
15424
+ const hasNoFilters = currentCohort ? isIndexedFilterSetEmpty(currentCohort?.filters) : true;
15425
+ const cancelEditing = ()=>{
15426
+ setIsEditing(false);
15427
+ setEditingLabel('');
15428
+ };
15429
+ const saveEdit = ()=>{
15430
+ if (editingLabel.trim() === '' || !currentCohort.name) return;
15431
+ dispatch(updateCohortName({
15432
+ id: currentCohort.id,
15433
+ name: editingLabel
15434
+ }));
15435
+ setIsEditing(false);
15436
+ setEditingLabel('');
15437
+ };
15438
+ const handleCreateNew = ()=>{
15439
+ dispatch(createNewCohort({
15440
+ name: 'New Cohort'
15441
+ }));
15442
+ };
15443
+ const selectData = useMemo(()=>{
15444
+ return allCohorts.map((cohort)=>({
15445
+ label: cohort.name,
15446
+ value: cohort.id
15447
+ }));
15448
+ }, [
15449
+ allCohorts
15450
+ ]);
15451
+ const onSelectCohort = useCallback((_value, option)=>{
15452
+ if (option) {
15453
+ dispatch(setCurrentCohortId(option.value));
15454
+ }
15455
+ }, [
15456
+ dispatch
15457
+ ]);
15458
+ const handleCohortAccessRequest = async (cohortId, values)=>{
15459
+ const cohort = allCohorts.find((obj)=>obj.id === cohortId);
15460
+ if (cohort) await submitCohortRequestAction(cohort, values, user, indexResources, remoteSupportService, appDispatch, getGetAggs, requestQuery);
15461
+ };
15462
+ if (loading) {
15463
+ return /*#__PURE__*/ jsx("div", {
15464
+ children: "Loading cohorts..."
15465
+ });
15466
+ }
15467
+ return /*#__PURE__*/ jsxs(Group, {
15468
+ gap: "xs",
15469
+ className: "flex pt-4 pl-2",
15470
+ children: [
15471
+ !isEditing ? /*#__PURE__*/ jsxs(Group, {
15472
+ children: [
15473
+ /*#__PURE__*/ jsx(Select, {
15474
+ data: selectData,
15475
+ value: currentCohort?.id ?? null,
15476
+ onChange: onSelectCohort
15477
+ }),
15478
+ /*#__PURE__*/ jsx(Tooltip, {
15479
+ label: "Rename selected cohort",
15480
+ position: "bottom",
15481
+ withArrow: true,
15482
+ children: /*#__PURE__*/ jsx(Button, {
15483
+ variant: "action",
15484
+ color: "secondary.4",
15485
+ onClick: startEditing,
15486
+ title: `Rename ${currentCohort?.name}`,
15487
+ "data-testid": "renameCohort",
15488
+ "aria-label": "Rename cohort",
15489
+ size: `compact-${size}`,
15490
+ children: /*#__PURE__*/ jsx(Icon, {
15491
+ icon: "gen3:edit",
15492
+ height: iconSize,
15493
+ width: iconSize
15494
+ })
15495
+ })
15496
+ })
15497
+ ]
15498
+ }) : /*#__PURE__*/ jsxs(Group, {
15499
+ gap: "xs",
15500
+ children: [
15501
+ /*#__PURE__*/ jsx(TextInput, {
15502
+ value: editingLabel,
15503
+ onChange: (e)=>setEditingLabel(e.target.value),
15504
+ placeholder: "Enter new name",
15505
+ onKeyDown: (e)=>{
15506
+ if (e.key === 'Enter' && editingLabel.trim()) saveEdit();
15507
+ if (e.key === 'Escape') cancelEditing();
15508
+ },
15509
+ autoFocus: true,
15510
+ style: {
15511
+ flex: 1
15512
+ }
15513
+ }),
15514
+ /*#__PURE__*/ jsx(Tooltip, {
15515
+ label: "Apply rename",
15516
+ position: "bottom",
15517
+ withArrow: true,
15518
+ children: /*#__PURE__*/ jsx(Button, {
15519
+ variant: "action",
15520
+ color: "secondary.4",
15521
+ onClick: saveEdit,
15522
+ "data-testid": "completeCohortRenameButton",
15523
+ "aria-label": "Complete rename cohort",
15524
+ disabled: editingLabel.trim() === '' || editingLabel.trim() === currentCohort.name,
15525
+ size: `compact-${size}`,
15526
+ children: /*#__PURE__*/ jsx(Icon, {
15527
+ icon: "gen3:check",
15528
+ height: iconSize,
15529
+ width: iconSize
15530
+ })
15531
+ })
15532
+ }),
15533
+ /*#__PURE__*/ jsx(Tooltip, {
15534
+ label: "Cancel rename",
15535
+ position: "bottom",
15536
+ withArrow: true,
15537
+ children: /*#__PURE__*/ jsx(Button, {
15538
+ variant: "action",
15539
+ "data-testid": "cancelRenameButton",
15540
+ "aria-label": "Cancel rename cohort",
15541
+ color: theme.colors.accent[4],
15542
+ onClick: cancelEditing,
15543
+ size: `compact-${size}`,
15544
+ children: /*#__PURE__*/ jsx(MdClose, {
15545
+ size: "1.5em",
15546
+ "aria-hidden": "true"
15547
+ })
15548
+ })
15549
+ })
15550
+ ]
15551
+ }),
15552
+ /*#__PURE__*/ jsx(Tooltip, {
15553
+ label: "Create a new cohort",
15554
+ position: "bottom",
15555
+ withArrow: true,
15556
+ children: /*#__PURE__*/ jsx(Button, {
15557
+ size: `compact-${size}`,
15558
+ "data-testid": "addButton",
15559
+ "aria-label": "Add cohort",
15560
+ variant: "action",
15561
+ color: "secondary.4",
15562
+ onClick: handleCreateNew,
15563
+ children: /*#__PURE__*/ jsx(MdAdd, {
15564
+ size: "1.5em",
15565
+ "aria-hidden": "true"
15566
+ })
15567
+ })
14484
15568
  }),
14485
- /*#__PURE__*/ jsx(Button, {
14486
- size: "sm",
14487
- variant: "outline",
14488
- color: "secondary.4",
14489
- disabled: !(index in filters) || Object.keys(filters[index].root).length == 0,
14490
- classNames: {
14491
- root: 'bg-base-max'
14492
- },
14493
- onClick: open,
14494
- children: "Save Cohort"
14495
- })
14496
- ]
14497
- });
14498
- };
14499
-
14500
- const ActionButtonGroup = ({ index })=>{
14501
- return /*#__PURE__*/ jsxs(Group, {
14502
- gap: "xs",
14503
- className: "flex pt-4 pl-2",
14504
- children: [
14505
- /*#__PURE__*/ jsx(SaveActionButton, {
14506
- index: index
15569
+ /*#__PURE__*/ jsx(Tooltip, {
15570
+ label: "Delete selected cohort",
15571
+ position: "bottom",
15572
+ withArrow: true,
15573
+ children: /*#__PURE__*/ jsx(Button, {
15574
+ size: `compact-${size}`,
15575
+ "data-testid": "uploadButton",
15576
+ "aria-label": "Upload cohort",
15577
+ variant: "action",
15578
+ onClick: ()=>{
15579
+ modals.openConfirmModal({
15580
+ title: `Delete Cohort: ${currentCohort?.name}`,
15581
+ centered: true,
15582
+ children: /*#__PURE__*/ jsxs(Text, {
15583
+ size: "sm",
15584
+ children: [
15585
+ "Are you sure you want to delete your cohort? Deleted cohorts cannot be restored.",
15586
+ ' ',
15587
+ 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.
15588
+ Are you sure you want to delete this cohort?.` : ''
15589
+ ]
15590
+ }),
15591
+ labels: {
15592
+ confirm: 'Delete Cohort',
15593
+ cancel: 'Cancel'
15594
+ },
15595
+ confirmProps: {
15596
+ color: theme.colors.accent[4]
15597
+ },
15598
+ onConfirm: ()=>{
15599
+ dispatch(removeCohort(currentCohort.id));
15600
+ }
15601
+ });
15602
+ },
15603
+ children: /*#__PURE__*/ jsx(Icon, {
15604
+ icon: "gen3:delete",
15605
+ height: iconSize,
15606
+ width: iconSize
15607
+ })
15608
+ })
14507
15609
  }),
14508
- /*#__PURE__*/ jsx(Button, {
14509
- size: "sm",
14510
- variant: "outline",
14511
- color: "secondary.4",
14512
- classNames: {
14513
- root: 'bg-base-max'
14514
- },
14515
- children: "Export Cohort"
15610
+ /*#__PURE__*/ jsx(Tooltip, {
15611
+ label: hasNoFilters ? 'No Filters defined. Cannot submit request.' : 'Submit Data Access Request',
15612
+ position: "bottom",
15613
+ withArrow: true,
15614
+ children: /*#__PURE__*/ jsx(Button, {
15615
+ size: `compact-${size}`,
15616
+ "data-testid": "cohortManager-requestCohortAccessButton",
15617
+ "aria-label": "Request cohort access",
15618
+ variant: "action",
15619
+ disabled: hasNoFilters,
15620
+ onClick: ()=>{
15621
+ const modelId = modals.open({
15622
+ title: 'DATA ACCESS REQUEST',
15623
+ children: /*#__PURE__*/ jsx(DataAccessRequestForm, {
15624
+ cohortId: currentCohort.id,
15625
+ submitFunction: handleCohortAccessRequest,
15626
+ close: ()=>modals.close(modelId)
15627
+ })
15628
+ });
15629
+ },
15630
+ children: /*#__PURE__*/ jsx(Icon, {
15631
+ icon: "gen3:request",
15632
+ height: iconSize,
15633
+ width: iconSize
15634
+ })
15635
+ })
14516
15636
  }),
14517
- /*#__PURE__*/ jsx(Button, {
14518
- size: "sm",
14519
- variant: "outline",
14520
- color: "secondary.4",
14521
- classNames: {
14522
- root: 'bg-base-max'
14523
- },
14524
- children: "Import Cohort"
14525
- })
15637
+ null
14526
15638
  ]
14527
15639
  });
14528
15640
  };
14529
15641
 
14530
- const CohortManager = ({ index })=>{
15642
+ const CohortQueryExpression = ({ index })=>{
14531
15643
  const currentCohortId = useAppSelector((state)=>selectCurrentCohortId(state));
14532
15644
  const currentCohortName = useAppSelector((state)=>selectCurrentCohortName(state));
14533
15645
  return /*#__PURE__*/ jsx("div", {
@@ -14574,7 +15686,7 @@ const CohortManager = ({ index })=>{
14574
15686
  });
14575
15687
  };
14576
15688
 
14577
- const IndexPanel = ({ dataConfig, tabs, tabTitle, emptySelection })=>{
15689
+ const IndexPanel = ({ dataConfig, tabs, tabTitle, emptySelection, indexResources, remoteSupportService })=>{
14578
15690
  const [activeFieldDefinitions, setActiveFieldDefinitions] = useState([]);
14579
15691
  const appDispatch = useAppDispatch();
14580
15692
  const index = dataConfig.dataType;
@@ -14599,7 +15711,7 @@ const IndexPanel = ({ dataConfig, tabs, tabTitle, emptySelection })=>{
14599
15711
  selectedFacets,
14600
15712
  facetDefinitions
14601
15713
  ]);
14602
- const cohortFilters = useAppSelector((state)=>selectIndexFilters(state, index));
15714
+ const cohortFilters = useAppSelector((state)=>selectCurrentCohortIndexFilters(state, index));
14603
15715
  const queryFields = categories.length === 0 ? fields : activeFieldDefinitions.map((x)=>x.field);
14604
15716
  const { data, isSuccess, isError: isAggsQueryError } = useRoundedAggsQuery({
14605
15717
  type: index,
@@ -14666,10 +15778,11 @@ const IndexPanel = ({ dataConfig, tabs, tabTitle, emptySelection })=>{
14666
15778
  }
14667
15779
  return /*#__PURE__*/ jsxs(Stack, {
14668
15780
  children: [
14669
- /*#__PURE__*/ jsx(CohortManager, {
15781
+ /*#__PURE__*/ jsx(CohortQueryExpression, {
14670
15782
  index: index
14671
15783
  }),
14672
15784
  /*#__PURE__*/ jsxs(Flex, {
15785
+ wrap: "nowrap",
14673
15786
  className: "flex h-full bg-base-light pb-4 ml-4",
14674
15787
  children: [
14675
15788
  /*#__PURE__*/ jsx(FacetSelectionPanel, {
@@ -14683,10 +15796,11 @@ const IndexPanel = ({ dataConfig, tabs, tabTitle, emptySelection })=>{
14683
15796
  }
14684
15797
  }),
14685
15798
  /*#__PURE__*/ jsxs(Stack, {
14686
- className: "w-2/3 mr-2 min-h-[500px]",
15799
+ className: "w-full md:w-[40rem] lg:w-[50rem] xl:w-[60rem] mr-2 min-h-[500px]",
14687
15800
  children: [
14688
- /*#__PURE__*/ jsx(ActionButtonGroup, {
14689
- index: index
15801
+ /*#__PURE__*/ jsx(CohortManager, {
15802
+ indexResources: indexResources,
15803
+ remoteSupportService: remoteSupportService
14690
15804
  }),
14691
15805
  selectedFacets.length > 0 ? /*#__PURE__*/ jsx(ChartsAndFacetsPanel, {
14692
15806
  index: index,
@@ -14721,147 +15835,6 @@ const IndexPanel = ({ dataConfig, tabs, tabTitle, emptySelection })=>{
14721
15835
  });
14722
15836
  };
14723
15837
 
14724
- const IconSize = {
14725
- xs: 14,
14726
- sm: 16,
14727
- md: 20,
14728
- lg: 24,
14729
- xl: 32,
14730
- 'xl-2': 36
14731
- };
14732
-
14733
- const DataAccessRequestForm = ({ cohortId, submitFunction, close })=>{
14734
- const [submitted, setSubmitted] = useState(false);
14735
- const form = useForm({
14736
- initialValues: {
14737
- name: '',
14738
- institution: '',
14739
- department: '',
14740
- address: '',
14741
- email: '',
14742
- phone: ''
14743
- },
14744
- validate: {
14745
- name: isNotEmpty('Name is required'),
14746
- institution: isNotEmpty('Institution is required'),
14747
- address: isNotEmpty('Address is required'),
14748
- email: isEmail('Please provide a valid email'),
14749
- phone: matches(/^[+]?[(]?[0-9]{3}[)]?[-\s.]?[0-9]{3}[-\s.]?[0-9]{4,6}$/, 'Please provide a valid phone number')
14750
- }
14751
- });
14752
- const handleSubmit = async (values)=>{
14753
- submitFunction(cohortId, values);
14754
- setSubmitted(true);
14755
- };
14756
- if (submitted) {
14757
- return /*#__PURE__*/ jsxs(Paper, {
14758
- shadow: "md",
14759
- p: "xl",
14760
- radius: "md",
14761
- children: [
14762
- /*#__PURE__*/ jsx(Title, {
14763
- order: 2,
14764
- mb: "md",
14765
- ta: "center",
14766
- children: "Request Submitted"
14767
- }),
14768
- /*#__PURE__*/ jsx(Text, {
14769
- ta: "center",
14770
- children: "Thank you for your data access request. We will review your submission and contact you shortly."
14771
- }),
14772
- /*#__PURE__*/ jsx(Group, {
14773
- justify: "center",
14774
- mt: "xl",
14775
- children: /*#__PURE__*/ jsx(Button, {
14776
- onClick: ()=>close(),
14777
- children: "Dismiss"
14778
- })
14779
- })
14780
- ]
14781
- });
14782
- }
14783
- return /*#__PURE__*/ jsxs(Paper, {
14784
- radius: "md",
14785
- children: [
14786
- /*#__PURE__*/ jsx(Text, {
14787
- c: "dimmed",
14788
- mb: "lg",
14789
- children: "Please complete all fields to request access to our research data."
14790
- }),
14791
- /*#__PURE__*/ jsx(Divider, {
14792
- mb: "lg"
14793
- }),
14794
- /*#__PURE__*/ jsx(Box, {
14795
- component: "form",
14796
- onSubmit: form.onSubmit(handleSubmit),
14797
- children: /*#__PURE__*/ jsxs(Stack, {
14798
- gap: "md",
14799
- children: [
14800
- /*#__PURE__*/ jsx(TextInput, {
14801
- label: "Full Name",
14802
- placeholder: "John Doe",
14803
- withAsterisk: true,
14804
- ...form.getInputProps('name')
14805
- }),
14806
- /*#__PURE__*/ jsx(TextInput, {
14807
- label: "Institution",
14808
- placeholder: "University, Organization, or Company",
14809
- withAsterisk: true,
14810
- ...form.getInputProps('institution')
14811
- }),
14812
- /*#__PURE__*/ jsx(TextInput, {
14813
- label: "Department",
14814
- placeholder: "Research Department",
14815
- ...form.getInputProps('department')
14816
- }),
14817
- /*#__PURE__*/ jsx(Textarea, {
14818
- label: "Address",
14819
- placeholder: "Full institutional address",
14820
- withAsterisk: true,
14821
- autosize: true,
14822
- minRows: 2,
14823
- maxRows: 4,
14824
- ...form.getInputProps('address')
14825
- }),
14826
- /*#__PURE__*/ jsx(TextInput, {
14827
- label: "Email",
14828
- placeholder: "your.email@institution.edu",
14829
- withAsterisk: true,
14830
- ...form.getInputProps('email')
14831
- }),
14832
- /*#__PURE__*/ jsx(TextInput, {
14833
- label: "Phone",
14834
- placeholder: "+1 (123) 456-7890",
14835
- withAsterisk: true,
14836
- ...form.getInputProps('phone')
14837
- }),
14838
- /*#__PURE__*/ jsxs(Group, {
14839
- justify: "flex-end",
14840
- children: [
14841
- /*#__PURE__*/ jsx(Button, {
14842
- type: "reset",
14843
- variant: "outline",
14844
- onClick: ()=>form.reset(),
14845
- children: "Reset"
14846
- }),
14847
- /*#__PURE__*/ jsx(Button, {
14848
- variant: "outline",
14849
- onClick: ()=>close(),
14850
- children: submitted ? 'Close' : 'Cancel'
14851
- }),
14852
- !submitted && /*#__PURE__*/ jsx(Button, {
14853
- type: "submit",
14854
- children: "Submit Request"
14855
- })
14856
- ]
14857
- })
14858
- ]
14859
- })
14860
- })
14861
- ]
14862
- });
14863
- };
14864
-
14865
15838
  /**
14866
15839
  * Formats a given date string into a specific format.
14867
15840
  *
@@ -14970,8 +15943,11 @@ const commonTableSettings$1 = (size = 'sm')=>{
14970
15943
  };
14971
15944
  };
14972
15945
 
14973
- const SavedCohortsTable = ({ size = 'md' })=>{
15946
+ const SavedCohortsTable = ({ indexResources, remoteSupportService, size = 'md' })=>{
14974
15947
  const appDispatch = useAppDispatch();
15948
+ const [getGetAggs] = useLazyGetAggsNoFilterSelfQuery();
15949
+ const [requestQuery] = useCreateRequestMutation();
15950
+ const user = useCoreSelector(selectUserDetails);
14975
15951
  const columns = useMemo(()=>[
14976
15952
  {
14977
15953
  accessorKey: 'name',
@@ -14984,7 +15960,7 @@ const SavedCohortsTable = ({ size = 'md' })=>{
14984
15960
  })
14985
15961
  },
14986
15962
  {
14987
- accessorKey: 'created_datetime',
15963
+ accessorKey: 'createdDatetime',
14988
15964
  header: 'Modified Date',
14989
15965
  Cell: ({ cell })=>/*#__PURE__*/ jsxs(Text, {
14990
15966
  children: [
@@ -14994,7 +15970,7 @@ const SavedCohortsTable = ({ size = 'md' })=>{
14994
15970
  })
14995
15971
  },
14996
15972
  {
14997
- accessorKey: 'modified_datetime',
15973
+ accessorKey: 'modifiedDatetime',
14998
15974
  header: 'Created Date',
14999
15975
  Cell: ({ cell })=>/*#__PURE__*/ jsxs(Text, {
15000
15976
  children: [
@@ -15025,15 +16001,9 @@ const SavedCohortsTable = ({ size = 'md' })=>{
15025
16001
  data,
15026
16002
  requestByCohortId
15027
16003
  ]);
15028
- const handleSubmission = (cohortId, values)=>{
15029
- if (cohortId && values) {
15030
- appDispatch(addDataAccessRequest({
15031
- cohortId,
15032
- userAccessInformation: values
15033
- }));
15034
- // Close the modal after successful dispatch
15035
- close();
15036
- }
16004
+ const handleCohortAccessRequest = async (cohortId, values)=>{
16005
+ const cohort = data.find((obj)=>obj.id === cohortId);
16006
+ if (cohort) await submitCohortRequestAction(cohort, values, user, indexResources, remoteSupportService, appDispatch, getGetAggs, requestQuery);
15037
16007
  };
15038
16008
  const table = useMantineReactTable({
15039
16009
  columns,
@@ -15041,23 +16011,26 @@ const SavedCohortsTable = ({ size = 'md' })=>{
15041
16011
  getRowId: (originalRow)=>originalRow.id,
15042
16012
  ...commonTableSettings$1(),
15043
16013
  positionActionsColumn: 'last',
15044
- renderRowActions: ({ row })=>/*#__PURE__*/ jsxs(Group, {
16014
+ renderRowActions: ({ row })=>{
16015
+ const hasNoFilters = isIndexedFilterSetEmpty(row.original.filters);
16016
+ return /*#__PURE__*/ jsxs(Group, {
15045
16017
  wrap: "nowrap",
15046
16018
  gap: "xs",
15047
16019
  children: [
15048
16020
  /*#__PURE__*/ jsx(Tooltip, {
15049
- label: "Submit Data Access Request",
16021
+ label: hasNoFilters ? 'No Filters defined. Cannot submit request.' : 'Submit Data Access Request',
15050
16022
  withArrow: true,
15051
16023
  children: /*#__PURE__*/ jsx(ActionIcon, {
15052
16024
  color: "accent.4",
15053
16025
  variant: "transparent",
16026
+ disabled: hasNoFilters,
15054
16027
  "aria-label": "Submit Data Access Request",
15055
16028
  onClick: ()=>{
15056
16029
  const modelId = modals.open({
15057
16030
  title: 'DATA ACCESS REQUEST',
15058
16031
  children: /*#__PURE__*/ jsx(DataAccessRequestForm, {
15059
16032
  cohortId: row.id,
15060
- submitFunction: handleSubmission,
16033
+ submitFunction: handleCohortAccessRequest,
15061
16034
  close: ()=>modals.close(modelId)
15062
16035
  })
15063
16036
  });
@@ -15097,16 +16070,21 @@ const SavedCohortsTable = ({ size = 'md' })=>{
15097
16070
  modals.openConfirmModal({
15098
16071
  title: 'Delete Cohort',
15099
16072
  centered: true,
15100
- children: /*#__PURE__*/ jsx(Text, {
16073
+ children: /*#__PURE__*/ jsxs(Text, {
15101
16074
  size: "sm",
15102
- children: "Are you sure you want to delete your cohort? Deleted cohorts cannot be restored."
16075
+ children: [
16076
+ "Are you sure you want to delete your cohort? Deleted cohorts cannot be restored.",
16077
+ ' ',
16078
+ row.original?.requested ? `This cohort has an access request. Deleting this cohort will results in not being able to correlate nay pending requests.
16079
+ Are you sure you want to delete this cohort?.` : ''
16080
+ ]
15103
16081
  }),
15104
16082
  labels: {
15105
16083
  confirm: 'Delete Cohort',
15106
16084
  cancel: 'Cancel'
15107
16085
  },
15108
16086
  confirmProps: {
15109
- color: theme.colors.utility[2]
16087
+ color: theme.colors.accent[4]
15110
16088
  },
15111
16089
  onConfirm: ()=>{
15112
16090
  appDispatch(removeCohort(row.id));
@@ -15122,7 +16100,8 @@ const SavedCohortsTable = ({ size = 'md' })=>{
15122
16100
  })
15123
16101
  })
15124
16102
  ]
15125
- })
16103
+ });
16104
+ }
15126
16105
  });
15127
16106
  return /*#__PURE__*/ jsx(Fragment, {
15128
16107
  children: /*#__PURE__*/ jsx("div", {
@@ -15134,20 +16113,95 @@ const SavedCohortsTable = ({ size = 'md' })=>{
15134
16113
  });
15135
16114
  };
15136
16115
 
15137
- const SavedCohortsPanel = ()=>{
16116
+ const SavedCohortsPanel = ({ indexResources, remoteSupportService })=>{
15138
16117
  return /*#__PURE__*/ jsx(Fragment, {
15139
16118
  children: /*#__PURE__*/ jsx(Stack, {
15140
16119
  className: "w-100 m-2",
15141
16120
  children: /*#__PURE__*/ jsx(Group, {
15142
16121
  className: "w-100 bg-base-light p-4",
15143
- children: /*#__PURE__*/ jsx(SavedCohortsTable, {})
16122
+ children: /*#__PURE__*/ jsx(SavedCohortsTable, {
16123
+ indexResources: indexResources,
16124
+ remoteSupportService: remoteSupportService
16125
+ })
15144
16126
  })
15145
16127
  })
15146
16128
  });
15147
16129
  };
15148
16130
 
16131
+ const useGetCohortRequests = (resources = [])=>{
16132
+ const [isLoading, setIsLoading] = useState(false);
16133
+ const [cohortRequests, setCohortRequests] = useState([]);
16134
+ const localRequests = useAppSelector(selectAllDataAccessRequests);
16135
+ const { data: submittedRequests, isFetching, isError, error } = useUserRequestQuery({
16136
+ resource_ids: resources
16137
+ });
16138
+ const cohortIdToNameMap = useAppSelector(selectCohortIdToNameMap);
16139
+ useEffect(()=>{
16140
+ // Return early if we're still fetching data
16141
+ if (isFetching) {
16142
+ setIsLoading(true);
16143
+ return;
16144
+ }
16145
+ // Once data is available, merge local and remote requests
16146
+ if (submittedRequests) {
16147
+ // Create a map of local requests by ID
16148
+ const localRequestsMap = localRequests.reduce((acc, localRequest)=>{
16149
+ acc[localRequest.id] = localRequest;
16150
+ return acc;
16151
+ }, {});
16152
+ // Add cohort names to the requests
16153
+ const requestsWithCohorts = submittedRequests.reduce((acc, request)=>{
16154
+ if (request.request_id && Object.hasOwn(localRequestsMap, request.request_id)) {
16155
+ const localRequest = localRequestsMap[request.request_id];
16156
+ acc.push({
16157
+ ...localRequest,
16158
+ cohortName: cohortIdToNameMap[localRequest.cohortId] ?? 'Not Found',
16159
+ status: request?.status ?? 'unknown',
16160
+ updatedDatetime: request.created_time ?? localRequest.updatedDatetime,
16161
+ createdDatetime: request.updated_time ?? localRequest.createdDatetime
16162
+ });
16163
+ } else {
16164
+ acc.push({
16165
+ ...request,
16166
+ cohortId: 'Not Set',
16167
+ email: request.username ?? 'Not Set',
16168
+ name: 'Not Set',
16169
+ organization: 'Not Set',
16170
+ id: request.request_id ?? 'Not Set',
16171
+ cohortName: 'Not Found',
16172
+ status: request?.status ?? 'unknown',
16173
+ updatedDatetime: request.created_time ?? 'Not Set',
16174
+ createdDatetime: request.updated_time ?? ' Not Set'
16175
+ });
16176
+ }
16177
+ return acc;
16178
+ }, []);
16179
+ setCohortRequests(requestsWithCohorts);
16180
+ } else if (isError) {
16181
+ // If there's an error fetching remote data, use local requests
16182
+ const localRequestsWithCohorts = localRequests.map((request)=>({
16183
+ ...request,
16184
+ cohortName: request.id ? cohortIdToNameMap[request.id] : 'Unknown'
16185
+ }));
16186
+ setCohortRequests(localRequestsWithCohorts);
16187
+ }
16188
+ setIsLoading(false);
16189
+ }, [
16190
+ submittedRequests,
16191
+ localRequests,
16192
+ isFetching,
16193
+ isError,
16194
+ cohortIdToNameMap
16195
+ ]);
16196
+ return {
16197
+ cohortRequests,
16198
+ isLoading: isLoading || isFetching,
16199
+ isError,
16200
+ error
16201
+ };
16202
+ };
15149
16203
  const RequestsTable = ()=>{
15150
- const requests = useAppSelector(selectAllDataAccessRequests);
16204
+ const { cohortRequests, isLoading, isError } = useGetCohortRequests(undefined);
15151
16205
  const columns = useMemo(()=>[
15152
16206
  {
15153
16207
  accessorKey: 'cohortName',
@@ -15160,7 +16214,7 @@ const RequestsTable = ()=>{
15160
16214
  })
15161
16215
  },
15162
16216
  {
15163
- accessorKey: 'request_datetime',
16217
+ accessorKey: 'createdDatetime',
15164
16218
  header: 'Request Date',
15165
16219
  Cell: ({ cell })=>/*#__PURE__*/ jsxs(Text, {
15166
16220
  children: [
@@ -15169,6 +16223,16 @@ const RequestsTable = ()=>{
15169
16223
  ]
15170
16224
  })
15171
16225
  },
16226
+ {
16227
+ accessorKey: 'updatedDatetime',
16228
+ header: 'Last Update',
16229
+ Cell: ({ cell })=>/*#__PURE__*/ jsxs(Text, {
16230
+ children: [
16231
+ formatDate(cell.getValue()),
16232
+ " "
16233
+ ]
16234
+ })
16235
+ },
15172
16236
  {
15173
16237
  accessorKey: 'status',
15174
16238
  header: 'Status',
@@ -15185,21 +16249,26 @@ const RequestsTable = ()=>{
15185
16249
  }
15186
16250
  }
15187
16251
  ], []);
15188
- const cohortIdToNameMap = useAppSelector(selectCohortIdToNameMap);
15189
- const requestsWithCohorts = useDeepCompareMemo(()=>requests.map((request)=>{
15190
- return {
15191
- ...request,
15192
- cohortName: cohortIdToNameMap[request.cohortId] || 'Unknown'
15193
- };
15194
- }), [
15195
- requests,
15196
- cohortIdToNameMap
15197
- ]);
16252
+ const size = 'sm';
15198
16253
  const table = useMantineReactTable({
15199
16254
  columns,
15200
- data: requestsWithCohorts,
15201
- ...commonTableSettings$1()
16255
+ data: cohortRequests ?? [],
16256
+ renderEmptyRowsFallback: ()=>/*#__PURE__*/ jsx(EmptyTableMessage, {
16257
+ message: 'No requests submitted.'
16258
+ }),
16259
+ ...commonTableSettings$1(size),
16260
+ state: {
16261
+ isLoading: isLoading
16262
+ }
15202
16263
  });
16264
+ if (isError) {
16265
+ return /*#__PURE__*/ jsx("div", {
16266
+ className: "w-full mt-24 flex flex-col items-center justify-center",
16267
+ children: /*#__PURE__*/ jsx(ErrorCard, {
16268
+ message: `Error retrieving user request`
16269
+ })
16270
+ });
16271
+ }
15203
16272
  return /*#__PURE__*/ jsx("div", {
15204
16273
  className: "inline-block overflow-x-scroll w-full",
15205
16274
  children: /*#__PURE__*/ jsx(MantineReactTable, {
@@ -15209,16 +16278,18 @@ const RequestsTable = ()=>{
15209
16278
  };
15210
16279
 
15211
16280
  const RequestsPanel = ()=>{
15212
- return /*#__PURE__*/ jsx(Stack, {
15213
- className: "w-100 m-2",
15214
- children: /*#__PURE__*/ jsx(Group, {
15215
- className: "w-100 bg-base-light p-4",
15216
- children: /*#__PURE__*/ jsx(RequestsTable, {})
16281
+ return /*#__PURE__*/ jsx(ProtectedContent, {
16282
+ children: /*#__PURE__*/ jsx(Stack, {
16283
+ className: "w-100 m-2",
16284
+ children: /*#__PURE__*/ jsx(Group, {
16285
+ className: "w-100 bg-base-light p-4",
16286
+ children: /*#__PURE__*/ jsx(RequestsTable, {})
16287
+ })
15217
16288
  })
15218
16289
  });
15219
16290
  };
15220
16291
 
15221
- const TabbedIndex = ({ config, tabsLayout = 'left' })=>{
16292
+ const TabbedIndex = ({ config, indexResources, tabsLayout = 'left' })=>{
15222
16293
  return /*#__PURE__*/ jsx("div", {
15223
16294
  className: "w-full",
15224
16295
  children: /*#__PURE__*/ jsxs(Tabs, {
@@ -15241,9 +16312,12 @@ const TabbedIndex = ({ config, tabsLayout = 'left' })=>{
15241
16312
  children: /*#__PURE__*/ jsx(IndexPanel, {
15242
16313
  dataConfig: panelConfig.dataConfig,
15243
16314
  tabTitle: panelConfig.tabTitle,
16315
+ resourceField: panelConfig.resourceField,
15244
16316
  tabs: panelConfig.tabs,
15245
16317
  emptySelection: config.emptySelection,
15246
- numColumns: panelConfig.numColumns ?? 2
16318
+ numColumns: panelConfig.numColumns ?? 2,
16319
+ indexResources: indexResources,
16320
+ remoteSupportService: config.remoteSupportService
15247
16321
  })
15248
16322
  }, `${panelConfig.tabTitle}-tabPanel`))
15249
16323
  ]
@@ -15251,8 +16325,20 @@ const TabbedIndex = ({ config, tabsLayout = 'left' })=>{
15251
16325
  });
15252
16326
  };
15253
16327
 
16328
+ const extractIndexResourceFromConfiguration = (config)=>{
16329
+ return config.dataIndexes.reduce((acc, panelConfig)=>{
16330
+ acc[panelConfig.dataConfig.dataType] = {
16331
+ resourceField: panelConfig.resourceField,
16332
+ resourcePath: panelConfig.resourcePath
16333
+ };
16334
+ return acc;
16335
+ }, {});
16336
+ };
16337
+
15254
16338
  const persistor = persistStore(AppStore);
15255
16339
  const CohortDiscovery = (config)=>{
16340
+ const indexResources = extractIndexResourceFromConfiguration(config);
16341
+ useAppDispatch(loadCohortsFromStorage());
15256
16342
  return /*#__PURE__*/ jsx(React__default.Fragment, {
15257
16343
  children: /*#__PURE__*/ jsxs(PersistGate, {
15258
16344
  persistor: persistor,
@@ -15266,7 +16352,7 @@ const CohortDiscovery = (config)=>{
15266
16352
  children: "Cohort Discovery"
15267
16353
  }),
15268
16354
  /*#__PURE__*/ jsxs(Tabs, {
15269
- keepMounted: true,
16355
+ keepMounted: false,
15270
16356
  defaultValue: "build",
15271
16357
  orientation: "vertical",
15272
16358
  classNames: {
@@ -15304,14 +16390,21 @@ const CohortDiscovery = (config)=>{
15304
16390
  tabTitle: config.dataIndexes[0].tabTitle,
15305
16391
  tabs: config.dataIndexes[0].tabs,
15306
16392
  emptySelection: config.emptySelection,
15307
- numColumns: config.dataIndexes[0].numColumns ?? 2
16393
+ resourceField: config.dataIndexes[0].resourceField,
16394
+ numColumns: config.dataIndexes[0].numColumns ?? 2,
16395
+ indexResources: indexResources,
16396
+ remoteSupportService: config.remoteSupportService
15308
16397
  }) : /*#__PURE__*/ jsx(TabbedIndex, {
15309
- config: config
16398
+ config: config,
16399
+ indexResources: indexResources
15310
16400
  })
15311
16401
  }),
15312
16402
  /*#__PURE__*/ jsx(Tabs.Panel, {
15313
16403
  value: "saved",
15314
- children: /*#__PURE__*/ jsx(SavedCohortsPanel, {})
16404
+ children: /*#__PURE__*/ jsx(SavedCohortsPanel, {
16405
+ indexResources: indexResources,
16406
+ remoteSupportService: config.remoteSupportService
16407
+ })
15315
16408
  }),
15316
16409
  /*#__PURE__*/ jsx(Tabs.Panel, {
15317
16410
  value: "request",
@@ -17788,7 +18881,8 @@ const DiscoveryPage = ({ headerProps, footerProps, discoveryConfig })=>{
17788
18881
  headerData: {
17789
18882
  title: 'Gen3 Discovery Page',
17790
18883
  content: 'Discovery Data',
17791
- key: 'gen3-discovery-page'
18884
+ key: 'gen3-discovery-page',
18885
+ ...discoveryConfig?.headerMetadata ? discoveryConfig.headerMetadata : {}
17792
18886
  },
17793
18887
  children: /*#__PURE__*/ jsx(Discovery, {
17794
18888
  discoveryConfig: discoveryConfig
@@ -17843,7 +18937,8 @@ const QueryPage = ({ headerProps, footerProps, queryProps })=>{
17843
18937
  headerData: {
17844
18938
  title: 'Gen3 Query Page',
17845
18939
  content: 'Query page',
17846
- key: 'gen3-query-page'
18940
+ key: 'gen3-query-page',
18941
+ ...queryProps?.headerMetadata ? queryProps.headerMetadata : {}
17847
18942
  },
17848
18943
  children: /*#__PURE__*/ jsx(QueryPanel, {
17849
18944
  graphQLEndpoint: queryProps.graphQLEndpoint
@@ -18015,7 +19110,8 @@ const LandingPage = ({ headerProps, footerProps, landingPage })=>{
18015
19110
  headerData: {
18016
19111
  title: 'Gen3 Home Page',
18017
19112
  content: 'Home page',
18018
- key: 'gen3-home-page'
19113
+ key: 'gen3-home-page',
19114
+ ...landingPage?.headerMetadata ? landingPage.headerMetadata : {}
18019
19115
  },
18020
19116
  children: /*#__PURE__*/ jsx("div", {
18021
19117
  className: "flex justify-items-center w-full",
@@ -18038,7 +19134,7 @@ const LandingPageGetServerSideProps = async ()=>{
18038
19134
  };
18039
19135
  };
18040
19136
 
18041
- const ExplorerPage = ({ headerProps, footerProps, explorerConfig, tabsLayout, sharedFiltersMap })=>{
19137
+ const ExplorerPage = ({ headerProps, footerProps, explorerConfig, headerMetadata, tabsLayout, sharedFiltersMap })=>{
18042
19138
  if (explorerConfig === undefined) {
18043
19139
  return /*#__PURE__*/ jsx(Center, {
18044
19140
  maw: 400,
@@ -18055,7 +19151,8 @@ const ExplorerPage = ({ headerProps, footerProps, explorerConfig, tabsLayout, sh
18055
19151
  headerData: {
18056
19152
  title: 'Gen3 Explorer Page',
18057
19153
  content: 'Explorer Page',
18058
- key: 'gen3-explorer-page'
19154
+ key: 'gen3-explorer-page',
19155
+ ...headerMetadata ? headerMetadata : {}
18059
19156
  },
18060
19157
  children: /*#__PURE__*/ jsx(CohortBuilder, {
18061
19158
  tabsLayout: tabsLayout,
@@ -18128,7 +19225,10 @@ const ExplorerPageGetServerSideProps = async ()=>{
18128
19225
  return {
18129
19226
  props: {
18130
19227
  ...await getNavPageLayoutPropsFromConfig(),
18131
- explorerConfig: cohortBuilderConfiguration
19228
+ explorerConfig: cohortBuilderConfiguration,
19229
+ headerMetadata: {
19230
+ ...cohortBuilderConfiguration?.headerMetadata
19231
+ }
18132
19232
  }
18133
19233
  };
18134
19234
  }
@@ -18139,6 +19239,7 @@ const ExplorerPageGetServerSideProps = async ()=>{
18139
19239
  sharedFiltersMap: sharedFiltersMap,
18140
19240
  tabsLayout: cohortBuilderConfiguration?.tabsLayout ?? 'left',
18141
19241
  explorerConfig: cohortBuilderConfiguration.explorerConfig,
19242
+ headerMetadata: cohortBuilderConfiguration.headerMetadata,
18142
19243
  accessControl: {
18143
19244
  ...DefaultAccessControlConfiguration,
18144
19245
  ...cohortBuilderConfiguration.accessControl ?? {}
@@ -18167,7 +19268,10 @@ const ExplorerPageGetServerSidePropsForConfigId = async (context)=>{
18167
19268
  return {
18168
19269
  props: {
18169
19270
  ...await getNavPageLayoutPropsFromConfig(),
18170
- explorerConfig: cohortBuilderConfiguration
19271
+ explorerConfig: cohortBuilderConfiguration,
19272
+ headerMetadata: {
19273
+ ...cohortBuilderConfiguration?.headerMetadata
19274
+ }
18171
19275
  }
18172
19276
  };
18173
19277
  }
@@ -18178,6 +19282,7 @@ const ExplorerPageGetServerSidePropsForConfigId = async (context)=>{
18178
19282
  sharedFiltersMap: sharedFiltersMap,
18179
19283
  tabsLayout: cohortBuilderConfiguration?.tabsLayout ?? 'left',
18180
19284
  explorerConfig: cohortBuilderConfiguration.explorerConfig,
19285
+ headerMetadata: cohortBuilderConfiguration.headerMetadata,
18181
19286
  accessControl: {
18182
19287
  ...DefaultAccessControlConfiguration,
18183
19288
  ...cohortBuilderConfiguration.accessControl ?? {}
@@ -19118,7 +20223,8 @@ const ProfilePage = ({ headerProps, footerProps, profileConfig })=>{
19118
20223
  headerData: {
19119
20224
  title: 'Gen3 Profile Page',
19120
20225
  content: 'Profile page',
19121
- key: 'gen3-profile-page'
20226
+ key: 'gen3-profile-page',
20227
+ ...profileConfig?.headerMetadata ? profileConfig.headerMetadata : {}
19122
20228
  },
19123
20229
  children: /*#__PURE__*/ jsx(Profile, {
19124
20230
  profileConfig: profileConfig
@@ -19145,7 +20251,8 @@ const LoginPage = ({ headerProps, footerProps, loginConfig })=>{
19145
20251
  headerData: {
19146
20252
  title: 'Gen3 Login Page',
19147
20253
  content: 'Login page',
19148
- key: 'gen3-login-page'
20254
+ key: 'gen3-login-page',
20255
+ ...loginConfig?.headerMetadata ? loginConfig.headerMetadata : {}
19149
20256
  },
19150
20257
  children: /*#__PURE__*/ jsx(LoginPanel, {
19151
20258
  ...loginConfig
@@ -19175,7 +20282,8 @@ const DictionaryPage = ({ headerProps, footerProps, config })=>{
19175
20282
  headerData: {
19176
20283
  title: 'Gen3 DataDictionary Page',
19177
20284
  content: 'Data Dictionary',
19178
- key: 'gen3-data-dictionary-page'
20285
+ key: 'gen3-data-dictionary-page',
20286
+ ...config?.headerMetadata ? config.headerMetadata : {}
19179
20287
  },
19180
20288
  children: /*#__PURE__*/ jsx(DictionaryWithContext, {
19181
20289
  config: config
@@ -20261,7 +21369,8 @@ const WorkspacePage = ({ headerProps, footerProps, workspaceProps })=>{
20261
21369
  headerData: {
20262
21370
  title: 'Gen3 Workspace Page',
20263
21371
  content: 'Workspace page',
20264
- key: 'gen3-workspace-page'
21372
+ key: 'gen3-workspace-page',
21373
+ ...workspaceProps?.headerMetadata ? workspaceProps.headerMetadata : {}
20265
21374
  },
20266
21375
  children: /*#__PURE__*/ jsx(Workspace, {
20267
21376
  config: workspaceProps
@@ -23426,7 +24535,8 @@ const CrosswalkPage = ({ headerProps, footerProps, config })=>{
23426
24535
  headerData: {
23427
24536
  title: 'Gen3 Crosswalk Page',
23428
24537
  content: 'Crosswalk Data',
23429
- key: 'gen3-crosswalk-page'
24538
+ key: 'gen3-crosswalk-page',
24539
+ ...config?.headerMetadata ? config.headerMetadata : {}
23430
24540
  },
23431
24541
  children: /*#__PURE__*/ jsx(CrosswalkPanel, {
23432
24542
  ...config
@@ -23816,5 +24926,5 @@ async function analysisApiCohortDiscovery(req, res) {
23816
24926
  }
23817
24927
  }
23818
24928
 
23819
- export { AISearchPageGetServerSideProps, AccessLevel, ActionBar, ActionButton, AdminAuthZPageGetServerSideProps, AiSearch, AISearchPage as AiSearchPage, AnalysisCenter, AnalysisCenterWithSections, AnalysisEditorPage, AnalysisEditorPageGetServerSideProps, AnalysisPage, AnalysisPageGetServerSideProps, AuthzPage, Banner, BaseModal, Charts, CohortBuilder, CohortManager$1 as CohortManager, CollapsableSidebar, ColorThemePage, ColorThemePageGetServerSideProps, ContentSource$1 as ContentSource, CreateCredentialsAPIKeyModal, CrosswalkPage, CrosswalkPageGetServerSideProps, Custom404Page, DataLibraryPage, DataLibraryPageGetServerSideProps, DictionaryPage, DictionaryPageGetServerSideProps, DiscoveryCellRendererFactory, DiscoveryProvider as DiscoveryConfigProvider, DiscoveryIndexPanel, DiscoveryPage, DiscoveryPageGetServerSideProps, DiscoveryRowRendererFactory, DropdownButton, EnumFacetChart, ErrorCard, ExplorerPage, ExplorerPageGetServerSideProps, ExplorerPageGetServerSidePropsForConfigId, ExplorerTableCellRendererFactory, ExplorerTableDetailsPanelFactory, FirstTimeModal, Footer, Gen3Button, Gen3ButtonReverse, Gen3Link, Gen3ModalsProvider, Gen3Provider, GqlQueryEditor$1 as GqlQueryEditor, Header$1 as Header, HoverLink, LandingPage, LandingPageGetServerSideProps, LoginButton$1 as LoginButton, LoginPage, LoginPageGetServerSideProps, LoginPanel, LoginProvidersPanel, MinutesToMilliseconds, NavPageLayout, NavigationBar, NavigationButton, NoAccessModal, NotebookLitePage, NotebookLitePageGetStaticProps, ProfilePage, ProfilePageGetServerSideProps, ProfileProvider, ProtectedContent, QueryExpression, QueryExpressionContext, QueryExpressionSection, QueryPage, QueryPageGetServerSideProps, ReactECharts, ResourcesFilters, SPECIAL_CASE_FIELDS, SessionExpiredModal, StudyGroup, SubmissionPage, SubmissionPageGetServerSideProps, TagCloud, TailwindConfig, TopBar, UploadJSONButton, User, Workspace, WorkspaceNoAccessPage, WorkspaceNoAccessPageServerSideProps, WorkspacePage, WorkspacePageGetServerSideProps, accessibleFieldName, analysisApiCohortDiscovery, capitalize, computeRowSpan, convertMinutesToMilliseconds, convertSecondsToMilliseconds, create10ColorAccessibleContrast, create10ColorPallet, createMantineTheme, credentialsLogin, credentialsLogout, extractObjectKey, focusStyles, getCurrentUnixTimestamp, getNavPageLayoutPropsFromConfig, getParamsValueAsString, getTagInfo, hasOwnProperty, isArrayOfString, isPropertyKey, isTextTransform$1 as isTextTransform, processLabel$1 as processLabel, processRangeKeyLabel, registerCohortBuilderDefaultPreviewRenderers, registerCohortDiscoveryApp, registerCohortSimilarityApp, registerDefaultDiscoveryDataLoaders, registerDiscoveryDefaultCellRenderers, registerDiscoveryDefaultStudyPreviewRenderers, registerExplorerDefaultCellRenderers, registerMetadataSchemaApp, removeKey$1 as removeKey, resourcePathFromProjectID, sessionLogout, sessionToken, stripTrailingSlash, toDisplayName, truncateString, unixTimeToString, useDiscoveryContext, userHasMethodForServiceOnResource$1 as userHasMethodForServiceOnResource };
24929
+ export { AISearchPageGetServerSideProps, AccessLevel, ActionBar, ActionButton, AdminAuthZPageGetServerSideProps, AiSearch, AISearchPage as AiSearchPage, AnalysisCenter, AnalysisCenterWithSections, AnalysisEditorPage, AnalysisEditorPageGetServerSideProps, AnalysisPage, AnalysisPageGetServerSideProps, AuthzPage, Banner, BaseModal, Charts, CohortBuilder, CohortManager$1 as CohortManager, CollapsableSidebar, ColorThemePage, ColorThemePageGetServerSideProps, ContentSource$1 as ContentSource, CreateCredentialsAPIKeyModal, CrosswalkPage, CrosswalkPageGetServerSideProps, Custom404Page, DataLibraryPage, DataLibraryPageGetServerSideProps, DictionaryPage, DictionaryPageGetServerSideProps, DiscoveryCellRendererFactory, DiscoveryProvider as DiscoveryConfigProvider, DiscoveryIndexPanel, DiscoveryPage, DiscoveryPageGetServerSideProps, DiscoveryRowRendererFactory, DropdownButton, EnumFacetChart, ErrorCard, ExplorerPage, ExplorerPageGetServerSideProps, ExplorerPageGetServerSidePropsForConfigId, ExplorerTableCellRendererFactory, ExplorerTableDetailsPanelFactory, FirstTimeModal, Footer, Gen3Button, Gen3ButtonReverse, Gen3Link, Gen3ModalsProvider, Gen3Provider, GqlQueryEditor$1 as GqlQueryEditor, Header$1 as Header, HoverLink, LandingPage, LandingPageGetServerSideProps, LoginButton$1 as LoginButton, LoginPage, LoginPageGetServerSideProps, LoginPanel, LoginProvidersPanel, MinutesToMilliseconds, NavPageLayout, NavigationBar, NavigationButton, NoAccessModal, NotebookLitePage, NotebookLitePageGetStaticProps, ProfilePage, ProfilePageGetServerSideProps, ProfileProvider, ProtectedContent, QueryExpression, QueryExpressionContext, QueryExpressionSection, QueryPage, QueryPageGetServerSideProps, ReactECharts, ResourcesFilters, SPECIAL_CASE_FIELDS, SessionExpiredModal, StudyGroup, SubmissionPage, SubmissionPageGetServerSideProps, TagCloud, TailwindConfig, TopBar, UploadJSONButton, User, Workspace, WorkspaceNoAccessPage, WorkspaceNoAccessPageServerSideProps, WorkspacePage, WorkspacePageGetServerSideProps, accessibleFieldName, analysisApiCohortDiscovery, capitalize, computeRowSpan, convertMinutesToMilliseconds, convertPathsToTree, convertSecondsToMilliseconds, create10ColorAccessibleContrast, create10ColorPallet, createMantineTheme, credentialsLogin, credentialsLogout, extractObjectKey, focusStyles, getCurrentUnixTimestamp, getNavPageLayoutPropsFromConfig, getParamsValueAsString, getTagInfo, hasOwnProperty, isArrayOfString, isPropertyKey, isTextTransform$1 as isTextTransform, processLabel$1 as processLabel, processRangeKeyLabel, registerCohortBuilderDefaultPreviewRenderers, registerCohortDiscoveryApp, registerCohortSimilarityApp, registerDefaultDiscoveryDataLoaders, registerDiscoveryDefaultCellRenderers, registerDiscoveryDefaultStudyPreviewRenderers, registerExplorerDefaultCellRenderers, registerMetadataSchemaApp, removeKey$1 as removeKey, resourcePathFromProjectID, sessionLogout, sessionToken, stripTrailingSlash, toDisplayName, truncateString, unixTimeToString, useDiscoveryContext, userHasMethodForServiceOnResource$1 as userHasMethodForServiceOnResource };
23820
24930
  //# sourceMappingURL=index.js.map