@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.
- package/dist/cjs/index.js +1698 -587
- package/dist/cjs/index.js.map +1 -1
- package/dist/dts/components/Content/LandingPageContent.d.ts +2 -1
- package/dist/dts/components/Content/LandingPageContent.d.ts.map +1 -1
- package/dist/dts/components/Content/LandingPageContent.js.map +1 -1
- package/dist/dts/components/Login/LoginPanel.d.ts.map +1 -1
- package/dist/dts/components/Login/LoginPanel.js +1 -0
- package/dist/dts/components/Login/LoginPanel.js.map +1 -1
- package/dist/dts/components/Login/types.d.ts +2 -1
- package/dist/dts/components/Login/types.d.ts.map +1 -1
- package/dist/dts/components/Login/types.js.map +1 -1
- package/dist/dts/components/MessageCards/index.d.ts +2 -1
- package/dist/dts/components/MessageCards/index.d.ts.map +1 -1
- package/dist/dts/components/MessageCards/index.js +2 -1
- package/dist/dts/components/MessageCards/index.js.map +1 -1
- package/dist/dts/components/Profile/types.d.ts +2 -1
- package/dist/dts/components/Profile/types.d.ts.map +1 -1
- package/dist/dts/components/charts/Charts.d.ts.map +1 -1
- package/dist/dts/components/charts/Charts.js +2 -0
- package/dist/dts/components/charts/Charts.js.map +1 -1
- package/dist/dts/components/charts/echarts/HorizontalBarChart.d.ts +1 -1
- package/dist/dts/components/charts/echarts/HorizontalBarChart.d.ts.map +1 -1
- package/dist/dts/components/charts/echarts/HorizontalBarChart.js +3 -2
- package/dist/dts/components/charts/echarts/HorizontalBarChart.js.map +1 -1
- package/dist/dts/components/charts/types.d.ts +2 -0
- package/dist/dts/components/charts/types.d.ts.map +1 -1
- package/dist/dts/components/facets/FacetControlsHeader.d.ts.map +1 -1
- package/dist/dts/components/facets/FacetControlsHeader.js +1 -1
- package/dist/dts/components/facets/FacetControlsHeader.js.map +1 -1
- package/dist/dts/components/facets/FacetSelector.d.ts.map +1 -1
- package/dist/dts/components/facets/FacetSelector.js +6 -6
- package/dist/dts/components/facets/FacetSelector.js.map +1 -1
- package/dist/dts/features/Analysis/AnalysisCardCompact.stories.d.ts +6 -6
- package/dist/dts/features/Analysis/types.d.ts +2 -1
- package/dist/dts/features/Analysis/types.d.ts.map +1 -1
- package/dist/dts/features/Analysis/types.js.map +1 -1
- package/dist/dts/features/CohortBuilder/CohortBuilder.d.ts.map +1 -1
- package/dist/dts/features/CohortBuilder/CohortBuilder.js +1 -2
- package/dist/dts/features/CohortBuilder/CohortBuilder.js.map +1 -1
- package/dist/dts/features/CohortBuilder/ExplorerTable/ArrayCellRenderer.d.ts +1 -1
- package/dist/dts/features/CohortBuilder/QueryExpression/QueryExpressionSection.js +1 -1
- package/dist/dts/features/CohortBuilder/QueryExpression/QueryExpressionSection.js.map +1 -1
- package/dist/dts/features/CohortBuilder/types.d.ts +2 -2
- package/dist/dts/features/CohortBuilder/types.d.ts.map +1 -1
- package/dist/dts/features/CohortDiscovery/ActionButtons/ActionButtonGroup.d.ts.map +1 -1
- package/dist/dts/features/CohortDiscovery/ActionButtons/ActionButtonGroup.js +18 -2
- package/dist/dts/features/CohortDiscovery/ActionButtons/ActionButtonGroup.js.map +1 -1
- package/dist/dts/features/CohortDiscovery/ActionButtons/SaveActionButton.d.ts.map +1 -1
- package/dist/dts/features/CohortDiscovery/ActionButtons/SaveActionButton.js +10 -4
- package/dist/dts/features/CohortDiscovery/ActionButtons/SaveActionButton.js.map +1 -1
- package/dist/dts/features/CohortDiscovery/ChartsAndFacetsPanel.js +2 -2
- package/dist/dts/features/CohortDiscovery/ChartsAndFacetsPanel.js.map +1 -1
- package/dist/dts/features/CohortDiscovery/CohortDiscovery.d.ts.map +1 -1
- package/dist/dts/features/CohortDiscovery/CohortDiscovery.js +8 -3
- package/dist/dts/features/CohortDiscovery/CohortDiscovery.js.map +1 -1
- package/dist/dts/features/CohortDiscovery/CohortManagment/CohortManager.d.ts +5 -2
- package/dist/dts/features/CohortDiscovery/CohortManagment/CohortManager.d.ts.map +1 -1
- package/dist/dts/features/CohortDiscovery/CohortManagment/CohortManager.js +120 -44
- package/dist/dts/features/CohortDiscovery/CohortManagment/CohortManager.js.map +1 -1
- package/dist/dts/features/CohortDiscovery/CohortManagment/CohortManagerSelectors.d.ts +44 -0
- package/dist/dts/features/CohortDiscovery/CohortManagment/CohortManagerSelectors.d.ts.map +1 -1
- package/dist/dts/features/CohortDiscovery/CohortManagment/CohortManagerSelectors.js +7 -1
- package/dist/dts/features/CohortDiscovery/CohortManagment/CohortManagerSelectors.js.map +1 -1
- package/dist/dts/features/CohortDiscovery/CohortManagment/CohortManagerSlice.d.ts +3 -1
- package/dist/dts/features/CohortDiscovery/CohortManagment/CohortManagerSlice.d.ts.map +1 -1
- package/dist/dts/features/CohortDiscovery/CohortManagment/CohortManagerSlice.js +71 -24
- package/dist/dts/features/CohortDiscovery/CohortManagment/CohortManagerSlice.js.map +1 -1
- package/dist/dts/features/CohortDiscovery/CohortManagment/CohortStorage.d.ts.map +1 -1
- package/dist/dts/features/CohortDiscovery/CohortManagment/CohortStorage.js +0 -2
- package/dist/dts/features/CohortDiscovery/CohortManagment/CohortStorage.js.map +1 -1
- package/dist/dts/features/CohortDiscovery/CohortQueryExpression.d.ts +6 -0
- package/dist/dts/features/CohortDiscovery/CohortQueryExpression.d.ts.map +1 -0
- package/dist/dts/features/CohortDiscovery/CohortQueryExpression.js +55 -0
- package/dist/dts/features/CohortDiscovery/CohortQueryExpression.js.map +1 -0
- package/dist/dts/features/CohortDiscovery/CohortSelectors.d.ts +0 -22
- package/dist/dts/features/CohortDiscovery/CohortSelectors.d.ts.map +1 -1
- package/dist/dts/features/CohortDiscovery/CohortSelectors.js +40 -34
- package/dist/dts/features/CohortDiscovery/CohortSelectors.js.map +1 -1
- package/dist/dts/features/CohortDiscovery/CohortSlice.d.ts +0 -90
- package/dist/dts/features/CohortDiscovery/CohortSlice.d.ts.map +1 -1
- package/dist/dts/features/CohortDiscovery/CohortSlice.js +161 -92
- package/dist/dts/features/CohortDiscovery/CohortSlice.js.map +1 -1
- package/dist/dts/features/CohortDiscovery/FacetSelectionPanel.d.ts.map +1 -1
- package/dist/dts/features/CohortDiscovery/FacetSelectionPanel.js +1 -1
- package/dist/dts/features/CohortDiscovery/FacetSelectionPanel.js.map +1 -1
- package/dist/dts/features/CohortDiscovery/FilterPanels/EnumFacetPanel.d.ts.map +1 -1
- package/dist/dts/features/CohortDiscovery/FilterPanels/EnumFacetPanel.js +5 -3
- package/dist/dts/features/CohortDiscovery/FilterPanels/EnumFacetPanel.js.map +1 -1
- package/dist/dts/features/CohortDiscovery/IndexPanel.d.ts +1 -1
- package/dist/dts/features/CohortDiscovery/IndexPanel.d.ts.map +1 -1
- package/dist/dts/features/CohortDiscovery/IndexPanel.js +8 -8
- package/dist/dts/features/CohortDiscovery/IndexPanel.js.map +1 -1
- package/dist/dts/features/CohortDiscovery/RequestManagerSlice.d.ts +14 -28
- package/dist/dts/features/CohortDiscovery/RequestManagerSlice.d.ts.map +1 -1
- package/dist/dts/features/CohortDiscovery/RequestManagerSlice.js +4 -4
- package/dist/dts/features/CohortDiscovery/RequestManagerSlice.js.map +1 -1
- package/dist/dts/features/CohortDiscovery/Requests/DataAccessRequestForm.d.ts.map +1 -1
- package/dist/dts/features/CohortDiscovery/Requests/DataAccessRequestForm.js +4 -10
- package/dist/dts/features/CohortDiscovery/Requests/DataAccessRequestForm.js.map +1 -1
- package/dist/dts/features/CohortDiscovery/Requests/RequestsPanel.d.ts.map +1 -1
- package/dist/dts/features/CohortDiscovery/Requests/RequestsPanel.js +2 -1
- package/dist/dts/features/CohortDiscovery/Requests/RequestsPanel.js.map +1 -1
- package/dist/dts/features/CohortDiscovery/Requests/RequestsTable.d.ts.map +1 -1
- package/dist/dts/features/CohortDiscovery/Requests/RequestsTable.js +76 -13
- package/dist/dts/features/CohortDiscovery/Requests/RequestsTable.js.map +1 -1
- package/dist/dts/features/CohortDiscovery/Requests/hooks.d.ts +3 -3
- package/dist/dts/features/CohortDiscovery/Requests/hooks.d.ts.map +1 -1
- package/dist/dts/features/CohortDiscovery/Requests/hooks.js.map +1 -1
- package/dist/dts/features/CohortDiscovery/Requests/submitCohortRequestAction.d.ts +9 -0
- package/dist/dts/features/CohortDiscovery/Requests/submitCohortRequestAction.d.ts.map +1 -0
- package/dist/dts/features/CohortDiscovery/Requests/submitCohortRequestAction.js +133 -0
- package/dist/dts/features/CohortDiscovery/Requests/submitCohortRequestAction.js.map +1 -0
- package/dist/dts/features/CohortDiscovery/SavedCohorts/SavedCohortsTable.d.ts.map +1 -1
- package/dist/dts/features/CohortDiscovery/SavedCohorts/SavedCohortsTable.js +25 -99
- package/dist/dts/features/CohortDiscovery/SavedCohorts/SavedCohortsTable.js.map +1 -1
- package/dist/dts/features/CohortDiscovery/TabbedIndex.d.ts +3 -2
- package/dist/dts/features/CohortDiscovery/TabbedIndex.d.ts.map +1 -1
- package/dist/dts/features/CohortDiscovery/TabbedIndex.js +2 -2
- package/dist/dts/features/CohortDiscovery/TabbedIndex.js.map +1 -1
- package/dist/dts/features/CohortDiscovery/appApi.d.ts +2 -4
- package/dist/dts/features/CohortDiscovery/appApi.d.ts.map +1 -1
- package/dist/dts/features/CohortDiscovery/appApi.js +6 -4
- package/dist/dts/features/CohortDiscovery/appApi.js.map +1 -1
- package/dist/dts/features/CohortDiscovery/hooks.d.ts.map +1 -1
- package/dist/dts/features/CohortDiscovery/hooks.js +7 -9
- package/dist/dts/features/CohortDiscovery/hooks.js.map +1 -1
- package/dist/dts/features/CohortDiscovery/queryApi.d.ts.map +1 -1
- package/dist/dts/features/CohortDiscovery/queryApi.js.map +1 -1
- package/dist/dts/features/CohortDiscovery/types.d.ts +47 -19
- package/dist/dts/features/CohortDiscovery/types.d.ts.map +1 -1
- package/dist/dts/features/CohortDiscovery/types.js +16 -6
- package/dist/dts/features/CohortDiscovery/types.js.map +1 -1
- package/dist/dts/features/CohortDiscovery/utils.d.ts +2 -0
- package/dist/dts/features/CohortDiscovery/utils.d.ts.map +1 -1
- package/dist/dts/features/CohortDiscovery/utils.js +9 -0
- package/dist/dts/features/CohortDiscovery/utils.js.map +1 -1
- package/dist/dts/features/Crosswalk/types.d.ts +2 -1
- package/dist/dts/features/Crosswalk/types.d.ts.map +1 -1
- package/dist/dts/features/Dictionary/types.d.ts +5 -1
- package/dist/dts/features/Dictionary/types.d.ts.map +1 -1
- package/dist/dts/features/Discovery/types.d.ts +2 -1
- package/dist/dts/features/Discovery/types.d.ts.map +1 -1
- package/dist/dts/features/Discovery/types.js.map +1 -1
- package/dist/dts/features/Navigation/TopBar/AccountButton.js +1 -1
- package/dist/dts/features/Navigation/TopBar/AccountButton.js.map +1 -1
- package/dist/dts/features/Navigation/TopBar/LoginButton.js +1 -1
- package/dist/dts/features/Navigation/TopBar/LoginButton.js.map +1 -1
- package/dist/dts/features/Workspace/types.d.ts +2 -1
- package/dist/dts/features/Workspace/types.d.ts.map +1 -1
- package/dist/dts/features/Workspace/types.js.map +1 -1
- package/dist/dts/lib/content/types.d.ts +2 -0
- package/dist/dts/lib/content/types.d.ts.map +1 -1
- package/dist/dts/lib/session/session.d.ts.map +1 -1
- package/dist/dts/lib/session/session.js +6 -2
- package/dist/dts/lib/session/session.js.map +1 -1
- package/dist/dts/pages/Crosswalk/Crosswalk.d.ts.map +1 -1
- package/dist/dts/pages/Crosswalk/Crosswalk.js +1 -5
- package/dist/dts/pages/Crosswalk/Crosswalk.js.map +1 -1
- package/dist/dts/pages/DataDictionary/DataDictionary.d.ts.map +1 -1
- package/dist/dts/pages/DataDictionary/DataDictionary.js +12 -5
- package/dist/dts/pages/DataDictionary/DataDictionary.js.map +1 -1
- package/dist/dts/pages/Discovery/Discovery.d.ts.map +1 -1
- package/dist/dts/pages/Discovery/Discovery.js +14 -5
- package/dist/dts/pages/Discovery/Discovery.js.map +1 -1
- package/dist/dts/pages/Explorer/Explorer.d.ts +1 -1
- package/dist/dts/pages/Explorer/Explorer.d.ts.map +1 -1
- package/dist/dts/pages/Explorer/Explorer.js +13 -6
- package/dist/dts/pages/Explorer/Explorer.js.map +1 -1
- package/dist/dts/pages/Explorer/data.d.ts.map +1 -1
- package/dist/dts/pages/Explorer/data.js +4 -4
- package/dist/dts/pages/Explorer/data.js.map +1 -1
- package/dist/dts/pages/Landing/Landing.d.ts.map +1 -1
- package/dist/dts/pages/Landing/Landing.js +12 -5
- package/dist/dts/pages/Landing/Landing.js.map +1 -1
- package/dist/dts/pages/Login/Login.d.ts.map +1 -1
- package/dist/dts/pages/Login/Login.js +1 -5
- package/dist/dts/pages/Login/Login.js.map +1 -1
- package/dist/dts/pages/Profile/Profile.d.ts.map +1 -1
- package/dist/dts/pages/Profile/Profile.js +12 -5
- package/dist/dts/pages/Profile/Profile.js.map +1 -1
- package/dist/dts/pages/Query/Query.d.ts.map +1 -1
- package/dist/dts/pages/Query/Query.js +12 -5
- package/dist/dts/pages/Query/Query.js.map +1 -1
- package/dist/dts/pages/Query/types.d.ts +2 -1
- package/dist/dts/pages/Query/types.d.ts.map +1 -1
- package/dist/dts/pages/Workspace/Workspace.d.ts.map +1 -1
- package/dist/dts/pages/Workspace/Workspace.js +14 -5
- package/dist/dts/pages/Workspace/Workspace.js.map +1 -1
- package/dist/dts/types/icons.d.ts +3 -2
- package/dist/dts/types/icons.d.ts.map +1 -1
- package/dist/dts/types/icons.js +3 -2
- package/dist/dts/types/icons.js.map +1 -1
- package/dist/dts/utils/index.d.ts +2 -3
- package/dist/dts/utils/index.d.ts.map +1 -1
- package/dist/dts/utils/index.js +2 -2
- package/dist/dts/utils/index.js.map +1 -1
- package/dist/dts/utils/tests/values.test.js +0 -1
- package/dist/dts/utils/tests/values.test.js.map +1 -1
- package/dist/esm/index.js +1710 -600
- package/dist/esm/index.js.map +1 -1
- package/dist/index.d.ts +186 -172
- package/dist/tsconfig.tsbuildinfo +1 -1
- 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
|
|
5
|
-
import { LoadingOverlay, Card, Group, Avatar, Text, Checkbox, Stack, Tooltip, UnstyledButton, ThemeIcon,
|
|
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,
|
|
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,
|
|
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 ||
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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/
|
|
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$
|
|
13516
|
+
const initialState$3 = {};
|
|
13444
13517
|
const expandSlice = createSlice({
|
|
13445
13518
|
name: 'CohortDiscovery/expandedCategory',
|
|
13446
|
-
initialState: initialState$
|
|
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$
|
|
13539
|
+
const initialState$2 = {};
|
|
13467
13540
|
const selectIndexFacets = createSlice({
|
|
13468
13541
|
name: 'CohortDiscovery/selectedFacets',
|
|
13469
|
-
initialState: initialState$
|
|
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
|
|
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
|
-
|
|
13654
|
-
|
|
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:
|
|
13662
|
-
|
|
13663
|
-
|
|
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
|
-
|
|
13670
|
-
const
|
|
13671
|
-
|
|
13672
|
-
|
|
13673
|
-
|
|
13674
|
-
|
|
13675
|
-
|
|
13676
|
-
|
|
13677
|
-
|
|
13678
|
-
|
|
13679
|
-
|
|
13680
|
-
|
|
13681
|
-
|
|
13682
|
-
|
|
13683
|
-
|
|
13684
|
-
|
|
13685
|
-
|
|
13686
|
-
|
|
13687
|
-
|
|
13688
|
-
|
|
13689
|
-
|
|
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
|
-
|
|
13693
|
-
|
|
13694
|
-
|
|
13695
|
-
|
|
13696
|
-
|
|
13697
|
-
|
|
13698
|
-
|
|
13699
|
-
|
|
13700
|
-
|
|
13701
|
-
|
|
13702
|
-
|
|
13703
|
-
|
|
13704
|
-
|
|
13705
|
-
|
|
13706
|
-
|
|
13707
|
-
|
|
13708
|
-
|
|
13709
|
-
|
|
13710
|
-
|
|
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
|
-
|
|
13737
|
-
|
|
13738
|
-
|
|
13739
|
-
|
|
13740
|
-
const
|
|
13741
|
-
|
|
13742
|
-
|
|
13743
|
-
|
|
13744
|
-
|
|
13745
|
-
|
|
13746
|
-
|
|
13747
|
-
}
|
|
13748
|
-
//
|
|
13749
|
-
|
|
13750
|
-
|
|
13751
|
-
|
|
13752
|
-
|
|
13753
|
-
|
|
13754
|
-
|
|
13755
|
-
|
|
13756
|
-
}
|
|
13757
|
-
|
|
13758
|
-
|
|
13759
|
-
|
|
13760
|
-
|
|
13761
|
-
|
|
13762
|
-
|
|
13763
|
-
|
|
13764
|
-
|
|
13765
|
-
|
|
13766
|
-
|
|
13767
|
-
|
|
13768
|
-
|
|
13769
|
-
|
|
13770
|
-
|
|
13771
|
-
|
|
13772
|
-
|
|
13773
|
-
})
|
|
13774
|
-
|
|
13775
|
-
|
|
13776
|
-
|
|
13777
|
-
|
|
13778
|
-
|
|
13779
|
-
|
|
13780
|
-
|
|
13781
|
-
|
|
13782
|
-
|
|
13783
|
-
|
|
13784
|
-
|
|
13785
|
-
|
|
13786
|
-
};
|
|
13787
|
-
|
|
13788
|
-
|
|
13789
|
-
|
|
13790
|
-
|
|
13791
|
-
|
|
13792
|
-
|
|
13793
|
-
|
|
13794
|
-
|
|
13795
|
-
|
|
13796
|
-
|
|
13797
|
-
|
|
13798
|
-
|
|
13799
|
-
|
|
13800
|
-
|
|
13801
|
-
|
|
13802
|
-
|
|
13803
|
-
|
|
13804
|
-
|
|
13805
|
-
|
|
13806
|
-
|
|
13807
|
-
|
|
13808
|
-
}
|
|
13809
|
-
|
|
13810
|
-
|
|
13811
|
-
}
|
|
13812
|
-
|
|
13813
|
-
|
|
13814
|
-
|
|
13815
|
-
|
|
13816
|
-
|
|
13817
|
-
|
|
13818
|
-
|
|
13819
|
-
|
|
13820
|
-
|
|
13821
|
-
|
|
13822
|
-
|
|
13823
|
-
|
|
13824
|
-
|
|
13825
|
-
|
|
13826
|
-
|
|
13827
|
-
|
|
13828
|
-
|
|
13829
|
-
|
|
13830
|
-
|
|
13831
|
-
|
|
13832
|
-
|
|
13833
|
-
|
|
13834
|
-
|
|
13835
|
-
|
|
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((
|
|
13841
|
-
|
|
13842
|
-
|
|
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
|
-
|
|
13880
|
-
|
|
13881
|
-
|
|
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)=>
|
|
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
|
|
14433
|
-
|
|
14434
|
-
|
|
14435
|
-
|
|
14436
|
-
|
|
14437
|
-
|
|
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(
|
|
14440
|
-
|
|
14441
|
-
|
|
14442
|
-
|
|
14443
|
-
|
|
14444
|
-
|
|
14445
|
-
|
|
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
|
-
|
|
15234
|
+
gap: "md",
|
|
14449
15235
|
children: [
|
|
14450
15236
|
/*#__PURE__*/ jsx(TextInput, {
|
|
14451
|
-
|
|
14452
|
-
|
|
14453
|
-
|
|
14454
|
-
|
|
14455
|
-
|
|
14456
|
-
|
|
14457
|
-
|
|
14458
|
-
|
|
14459
|
-
|
|
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:
|
|
14468
|
-
children: "
|
|
15260
|
+
onClick: ()=>form.reset(),
|
|
15261
|
+
children: "Reset"
|
|
14469
15262
|
}),
|
|
14470
15263
|
/*#__PURE__*/ jsx(Button, {
|
|
14471
|
-
|
|
14472
|
-
|
|
14473
|
-
|
|
14474
|
-
|
|
14475
|
-
|
|
14476
|
-
|
|
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(
|
|
14486
|
-
|
|
14487
|
-
|
|
14488
|
-
|
|
14489
|
-
|
|
14490
|
-
|
|
14491
|
-
|
|
14492
|
-
|
|
14493
|
-
|
|
14494
|
-
|
|
14495
|
-
|
|
14496
|
-
|
|
14497
|
-
|
|
14498
|
-
|
|
14499
|
-
|
|
14500
|
-
|
|
14501
|
-
|
|
14502
|
-
|
|
14503
|
-
|
|
14504
|
-
|
|
14505
|
-
|
|
14506
|
-
|
|
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(
|
|
14509
|
-
|
|
14510
|
-
|
|
14511
|
-
|
|
14512
|
-
|
|
14513
|
-
|
|
14514
|
-
|
|
14515
|
-
|
|
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
|
-
|
|
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
|
|
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)=>
|
|
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(
|
|
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-
|
|
15799
|
+
className: "w-full md:w-[40rem] lg:w-[50rem] xl:w-[60rem] mr-2 min-h-[500px]",
|
|
14687
15800
|
children: [
|
|
14688
|
-
/*#__PURE__*/ jsx(
|
|
14689
|
-
|
|
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: '
|
|
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: '
|
|
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
|
|
15029
|
-
|
|
15030
|
-
|
|
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 })
|
|
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:
|
|
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:
|
|
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__*/
|
|
16073
|
+
children: /*#__PURE__*/ jsxs(Text, {
|
|
15101
16074
|
size: "sm",
|
|
15102
|
-
children:
|
|
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.
|
|
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
|
|
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: '
|
|
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
|
|
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:
|
|
15201
|
-
|
|
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(
|
|
15213
|
-
|
|
15214
|
-
|
|
15215
|
-
|
|
15216
|
-
|
|
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:
|
|
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
|
-
|
|
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
|