@gen3/core 0.11.33 → 0.11.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/cjs/index.js +624 -554
  2. package/dist/cjs/index.js.map +1 -1
  3. package/dist/cjs/server.js +45 -0
  4. package/dist/cjs/server.js.map +1 -0
  5. package/dist/dts/constants.d.ts +1 -0
  6. package/dist/dts/constants.d.ts.map +1 -1
  7. package/dist/dts/features/cart/cartSelectors.d.ts +129 -0
  8. package/dist/dts/features/cart/cartSelectors.d.ts.map +1 -0
  9. package/dist/dts/features/cart/cartSlice.d.ts +24 -0
  10. package/dist/dts/features/cart/cartSlice.d.ts.map +1 -0
  11. package/dist/dts/features/cart/index.d.ts +5 -0
  12. package/dist/dts/features/cart/index.d.ts.map +1 -0
  13. package/dist/dts/features/cart/test/cartSelector.unit.test.d.ts +2 -0
  14. package/dist/dts/features/cart/test/cartSelector.unit.test.d.ts.map +1 -0
  15. package/dist/dts/features/cohort/cohortManagerSelector.d.ts +4 -0
  16. package/dist/dts/features/cohort/cohortManagerSelector.d.ts.map +1 -1
  17. package/dist/dts/features/cohort/cohortManagerSlice.d.ts +1 -0
  18. package/dist/dts/features/cohort/cohortManagerSlice.d.ts.map +1 -1
  19. package/dist/dts/features/cohort/utils.d.ts +13 -0
  20. package/dist/dts/features/cohort/utils.d.ts.map +1 -1
  21. package/dist/dts/features/filters/types.d.ts +7 -0
  22. package/dist/dts/features/filters/types.d.ts.map +1 -1
  23. package/dist/dts/features/user/userSliceRTK.d.ts +3 -0
  24. package/dist/dts/features/user/userSliceRTK.d.ts.map +1 -1
  25. package/dist/dts/hooks.d.ts +2 -0
  26. package/dist/dts/hooks.d.ts.map +1 -1
  27. package/dist/dts/index.d.ts +3 -2
  28. package/dist/dts/index.d.ts.map +1 -1
  29. package/dist/dts/reducers.d.ts +2 -0
  30. package/dist/dts/reducers.d.ts.map +1 -1
  31. package/dist/dts/server.d.ts +4 -0
  32. package/dist/dts/server.d.ts.map +1 -0
  33. package/dist/dts/store.d.ts +4 -0
  34. package/dist/dts/store.d.ts.map +1 -1
  35. package/dist/esm/index.js +613 -555
  36. package/dist/esm/index.js.map +1 -1
  37. package/dist/esm/server.js +29 -0
  38. package/dist/esm/server.js.map +1 -0
  39. package/dist/index.d.ts +385 -208
  40. package/dist/server.d.ts +31 -0
  41. package/package.json +7 -2
package/dist/cjs/index.js CHANGED
@@ -8,9 +8,9 @@ var reactRedux = require('react-redux');
8
8
  var React = require('react');
9
9
  var graphql = require('graphql');
10
10
  var jsonpathPlus = require('jsonpath-plus');
11
+ var lodash = require('lodash');
11
12
  var nanoid$1 = require('nanoid');
12
13
  var useSWR = require('swr');
13
- var lodash = require('lodash');
14
14
  var flat = require('flat');
15
15
  var Papa = require('papaparse');
16
16
  var reduxPersist = require('redux-persist');
@@ -72,6 +72,7 @@ const FILE_DELIMITERS = {
72
72
  tsv: '\t',
73
73
  csv: ','
74
74
  };
75
+ const CART_LIMIT = 10000;
75
76
 
76
77
  const isFetchError = (obj)=>{
77
78
  if (typeof obj !== 'object' || obj === null) {
@@ -426,7 +427,7 @@ const useCoreDispatch = reactRedux.useDispatch.withTypes();
426
427
  });
427
428
  const isAuthenticated = (loginStatus)=>loginStatus === 'authenticated';
428
429
  const isPending = (loginStatus)=>loginStatus === 'pending';
429
- const initialState$8 = {
430
+ const initialState$9 = {
430
431
  status: 'uninitialized',
431
432
  loginStatus: 'unauthenticated',
432
433
  error: undefined
@@ -437,9 +438,9 @@ const initialState$8 = {
437
438
  * @returns: status messages wrapped around fetchUserState response dict
438
439
  */ const slice$4 = toolkit.createSlice({
439
440
  name: 'fence/user',
440
- initialState: initialState$8,
441
+ initialState: initialState$9,
441
442
  reducers: {
442
- resetUserState: ()=>initialState$8
443
+ resetUserState: ()=>initialState$9
443
444
  },
444
445
  extraReducers: (builder)=>{
445
446
  builder.addCase(fetchUserState.fulfilled, (_, action)=>{
@@ -581,11 +582,11 @@ const { useGetExternalLoginsQuery, useLazyGetExternalLoginsQuery, useLazyIsExter
581
582
  }
582
583
  };
583
584
 
584
- const initialState$7 = {};
585
+ const initialState$8 = {};
585
586
  // TODO: document what this does
586
587
  const slice$3 = toolkit.createSlice({
587
588
  name: 'drsResolver',
588
- initialState: initialState$7,
589
+ initialState: initialState$8,
589
590
  reducers: {
590
591
  setDRSHostnames: (_state, action)=>{
591
592
  return action.payload;
@@ -607,12 +608,12 @@ const lookupGen3App = (id)=>{
607
608
  else return null;
608
609
  };
609
610
 
610
- const initialState$6 = {
611
+ const initialState$7 = {
611
612
  gen3Apps: {}
612
613
  };
613
614
  const slice$2 = toolkit.createSlice({
614
615
  name: 'gen3Apps',
615
- initialState: initialState$6,
616
+ initialState: initialState$7,
616
617
  reducers: {
617
618
  addGen3AppMetadata: (state, action)=>{
618
619
  const { name, requiredEntityTypes } = action.payload;
@@ -643,13 +644,13 @@ const selectGen3AppByName = (appName)=>lookupGen3App(appName); // TODO: memoize
643
644
  Modals["GeneralErrorModal"] = "GeneralErrorModal";
644
645
  return Modals;
645
646
  }({});
646
- const initialState$5 = {
647
+ const initialState$6 = {
647
648
  currentModal: null
648
649
  };
649
650
  //Creates a modal slice for tracking showModal and hideModal state.
650
651
  const slice$1 = toolkit.createSlice({
651
652
  name: 'modals',
652
- initialState: initialState$5,
653
+ initialState: initialState$6,
653
654
  reducers: {
654
655
  showModal: (state, action)=>{
655
656
  state.currentModal = action.payload.modal;
@@ -722,7 +723,7 @@ const getTimestamp = ()=>{
722
723
  };
723
724
 
724
725
  const NO_WORKSPACE_ID = 'none';
725
- const initialState$4 = {
726
+ const initialState$5 = {
726
727
  id: NO_WORKSPACE_ID,
727
728
  status: WorkspaceStatus.NotFound,
728
729
  requestedStatus: RequestedWorkspaceStatus.Unset,
@@ -730,7 +731,7 @@ const initialState$4 = {
730
731
  };
731
732
  const slice = toolkit.createSlice({
732
733
  name: 'ActiveWorkspace',
733
- initialState: initialState$4,
734
+ initialState: initialState$5,
734
735
  reducers: {
735
736
  setActiveWorkspaceId: (state, action)=>{
736
737
  state = {
@@ -772,6 +773,25 @@ const selectActiveWorkspaceStatus = (state)=>state.activeWorkspace.status;
772
773
  const selectRequestedWorkspaceStatus = (state)=>state.activeWorkspace.requestedStatus;
773
774
  const selectRequestedWorkspaceStatusTimestamp = (state)=>state.activeWorkspace.requestedStatusTimestamp;
774
775
 
776
+ const cartAdapter = toolkit.createEntityAdapter({
777
+ selectId: (item)=>item.id
778
+ });
779
+ const initialState$4 = cartAdapter.getInitialState({});
780
+ const cartSlice = toolkit.createSlice({
781
+ name: 'cart',
782
+ initialState: initialState$4,
783
+ reducers: {
784
+ addItemsToCart: cartAdapter.addMany,
785
+ removeItemsFromCart: cartAdapter.removeMany
786
+ }
787
+ });
788
+ const cartReducer = cartSlice.reducer;
789
+ const { addItemsToCart, removeItemsFromCart } = cartSlice.actions;
790
+
791
+ const { selectById: selectCartItem, selectIds: selectCartItems, selectAll: selectCart, selectTotal: selectCartCount } = cartAdapter.getSelectors((state)=>state.cart);
792
+
793
+ const cartReducerPath = 'cart';
794
+
775
795
  /**
776
796
  * Creates a base class core API for guppy API calls.
777
797
  * @returns: guppy core API with guppyAPIFetch base query
@@ -829,578 +849,176 @@ const guppyAPISliceMiddleware = guppyApi.middleware;
829
849
  const guppyApiSliceReducerPath = guppyApi.reducerPath;
830
850
  const guppyApiReducer = guppyApi.reducer;
831
851
 
832
- const defaultCohortNameGenerator = ()=>`Custom cohort ${new Date().toLocaleString('en-CA', {
833
- timeZone: 'America/Chicago',
834
- hour12: false
835
- }).replace(',', '')}`;
836
- const isNameUnique = (entities, name, excludeId)=>{
837
- const trimmedName = name.trim();
838
- if (!trimmedName) return false;
839
- return !entities.some((cohort)=>cohort && cohort.id !== excludeId && cohort.name.trim().toLowerCase() === trimmedName.toLowerCase());
852
+ const isOperationWithField = (operation)=>{
853
+ return operation?.field !== undefined;
840
854
  };
841
- const generateUniqueName = (entities, baseName)=>{
842
- const trimmedBaseName = baseName.trim();
843
- // If base name is unique, use it
844
- if (isNameUnique(entities, trimmedBaseName)) {
845
- return trimmedBaseName;
855
+ const isOperatorWithFieldAndArrayOfOperands = (operation)=>{
856
+ if (typeof operation === 'object' && operation !== null && 'operands' in operation && Array.isArray(operation.operands) && 'field' in operation && typeof operation.field === 'string' // Assuming `field` should be a string
857
+ ) {
858
+ const { operator } = operation.operator;
859
+ return operator === 'in' || operator === 'exclude' || operator === 'excludeifany';
860
+ }
861
+ return false;
862
+ };
863
+ const extractFilterValue = (op)=>{
864
+ const valueExtractorHandler = new ValueExtractorHandler();
865
+ return handleOperation(valueExtractorHandler, op);
866
+ };
867
+ const extractEnumFilterValue = (op)=>{
868
+ const enumValueExtractorHandler = new EnumValueExtractorHandler();
869
+ const results = handleOperation(enumValueExtractorHandler, op);
870
+ return results ?? [];
871
+ };
872
+ const assertNever = (x)=>{
873
+ throw Error(`Exhaustive comparison did not handle: ${x}`);
874
+ };
875
+ const handleOperation = (handler, op)=>{
876
+ switch(op.operator){
877
+ case '=':
878
+ return handler.handleEquals(op);
879
+ case '!=':
880
+ return handler.handleNotEquals(op);
881
+ case '<':
882
+ return handler.handleLessThan(op);
883
+ case '<=':
884
+ return handler.handleLessThanOrEquals(op);
885
+ case '>':
886
+ return handler.handleGreaterThan(op);
887
+ case '>=':
888
+ return handler.handleGreaterThanOrEquals(op);
889
+ case 'and':
890
+ return handler.handleIntersection(op);
891
+ case 'or':
892
+ return handler.handleUnion(op);
893
+ case 'nested':
894
+ return handler.handleNestedFilter(op);
895
+ case 'in':
896
+ case 'includes':
897
+ return handler.handleIncludes(op);
898
+ case 'excludeifany':
899
+ return handler.handleExcludeIfAny(op);
900
+ case 'excludes':
901
+ return handler.handleExcludes(op);
902
+ case 'exists':
903
+ return handler.handleExists(op);
904
+ case 'missing':
905
+ return handler.handleMissing(op);
906
+ default:
907
+ return assertNever(op);
846
908
  }
847
- // Find a unique name by appending numbers
848
- let counter = 1;
849
- let uniqueName;
850
- do {
851
- uniqueName = `${trimmedBaseName} (${counter})`;
852
- counter++;
853
- }while (!isNameUnique(entities, uniqueName))
854
- return uniqueName;
855
909
  };
856
-
857
910
  /**
858
- * Cohorts in Gen3 are defined as a set of filters for each index in the data.
859
- * This means one cohort id defined for all "tabs" in CohortBuilder (explorer)
860
- * Switching a cohort id means all the cohorts for the index are changed.
861
- */ const DEFAULT_COHORT_NAME = 'Cohort';
862
- const newCohort = ({ filters = {}, customName })=>{
863
- const ts = new Date().toISOString();
864
- const newName = customName ?? defaultCohortNameGenerator();
865
- const newId = createCohortId();
866
- return {
867
- name: newName,
868
- id: newId,
869
- filters: filters ?? {},
870
- modified: false,
871
- saved: false,
872
- createdDatetime: ts,
873
- modifiedDatetime: ts,
874
- counts: {}
875
- };
911
+ * Return true if a FilterSet's root value is an empty object
912
+ * @param fs - FilterSet to test
913
+ */ const isFilterEmpty = (fs)=>lodash.isEqual({}, fs);
914
+ /**
915
+ * Type guard to check if an object is a GQLIntersection
916
+ * @param value - The value to check
917
+ * @returns True if the value is a GQLIntersection
918
+ */ const isGQLIntersection = (value)=>{
919
+ return typeof value === 'object' && value !== null && 'and' in value && Array.isArray(value.and);
876
920
  };
877
- const nanoid = nanoid$1.customAlphabet('1234567890abcdef', 16);
878
- const createCohortId = ()=>nanoid();
879
- const cohortsAdapter = toolkit.createEntityAdapter({
880
- sortComparer: (a, b)=>{
881
- if (a.modifiedDatetime <= b.modifiedDatetime) return 1;
882
- else return -1;
883
- },
884
- selectId: (cohort)=>cohort.id
885
- });
886
- // Create an initial unsaved cohort
887
- const initialCohort = newCohort({
888
- customName: DEFAULT_COHORT_NAME
889
- });
890
- const emptyInitialState = cohortsAdapter.getInitialState({
891
- currentCohortId: initialCohort.id,
892
- message: undefined
893
- });
894
- // Set the initial cohort in the adapter state
895
- const initialState$3 = cohortsAdapter.setOne(emptyInitialState, initialCohort);
896
- const getCurrentCohortId = (state)=>state.currentCohortId;
897
921
  /**
898
- * Redux slice for cohort filters
899
- */ const cohortManagerSlice = toolkit.createSlice({
900
- name: 'cohort',
901
- initialState: initialState$3,
902
- reducers: {
903
- createNewCohort: (state, action)=>{
904
- const baseName = action.payload.name || `Cohort`;
905
- const uniqueName = generateUniqueName(Object.values(state.entities), baseName);
906
- const cohort = newCohort({
907
- filters: action.payload.filters,
908
- customName: uniqueName
922
+ * Type guard to check if an object is a GQLIntersection
923
+ * @param value - The value to check
924
+ * @returns True if the value is a GQLIntersection
925
+ */ const isGQLUnion = (value)=>{
926
+ return typeof value === 'object' && value !== null && 'or' in value && Array.isArray(value.or);
927
+ };
928
+ class ToGqlHandler {
929
+ constructor(){
930
+ this.handleEquals = (op)=>({
931
+ '=': {
932
+ [op.field]: op.operand
933
+ }
909
934
  });
910
- cohortsAdapter.addOne(state, cohort);
911
- state.currentCohortId = cohort.id;
912
- },
913
- updateCohortName: (state, action)=>{
914
- const { id, name } = action.payload;
915
- cohortsAdapter.updateOne(state, {
916
- id: id,
917
- changes: {
918
- name: name,
919
- modified: true,
920
- modifiedDatetime: new Date().toISOString()
935
+ this.handleNotEquals = (op)=>({
936
+ '!=': {
937
+ [op.field]: op.operand
921
938
  }
922
939
  });
923
- },
924
- removeCohort: (state, action)=>{
925
- const { id: cohortId } = action.payload;
926
- const removedCohortName = state.entities[cohortId].name;
927
- const totalCohorts = Object.keys(state.entities).length;
928
- if (totalCohorts <= 1) {
929
- cohortsAdapter.removeAll(state);
930
- const defaultCohort = newCohort({
931
- filters: {},
932
- customName: DEFAULT_COHORT_NAME
933
- });
934
- cohortsAdapter.addOne(state, defaultCohort);
935
- state.currentCohortId = defaultCohort.id;
936
- if (action?.payload.shouldShowMessage) {
937
- state.message = [
938
- `deleteCohort|${removedCohortName}|${state.currentCohortId}`
939
- ];
940
+ this.handleLessThan = (op)=>({
941
+ '<': {
942
+ [op.field]: op.operand
940
943
  }
941
- return;
942
- }
943
- cohortsAdapter.removeOne(state, cohortId);
944
- // deleted the current cohort so set to the most recent cohort
945
- if (state.currentCohortId === cohortId) {
946
- const remainingIds = Object.keys(state.entities);
947
- state.currentCohortId = remainingIds[0];
948
- }
949
- if (action?.payload.shouldShowMessage) {
950
- state.message = [
951
- `deleteCohort|${removedCohortName}|${state.currentCohortId}`
952
- ];
953
- }
954
- },
955
- // adds a filter to the cohort filter set at the given index
956
- updateCohortFilter: (state, action)=>{
957
- const { index, field, filter } = action.payload;
958
- const currentCohortId = getCurrentCohortId(state);
959
- if (!state.entities[currentCohortId]) {
960
- return;
961
- }
962
- cohortsAdapter.updateOne(state, {
963
- id: currentCohortId,
964
- changes: {
965
- filters: {
966
- ...state.entities[currentCohortId].filters,
967
- [index]: {
968
- mode: state.entities[currentCohortId]?.filters[index]?.mode ?? 'and',
969
- root: {
970
- ...state.entities[currentCohortId]?.filters[index]?.root ?? {},
971
- [field]: filter
972
- }
973
- }
974
- },
975
- modified: true,
976
- modifiedDatetime: new Date().toISOString()
944
+ });
945
+ this.handleLessThanOrEquals = (op)=>({
946
+ '<=': {
947
+ [op.field]: op.operand
977
948
  }
978
949
  });
979
- },
980
- setCohortFilter: (state, action)=>{
981
- const { index, filters } = action.payload;
982
- const currentCohortId = getCurrentCohortId(state);
983
- if (!state.entities[currentCohortId]) {
984
- console.error(`no cohort with id=${currentCohortId} defined`);
985
- return;
986
- }
987
- cohortsAdapter.updateOne(state, {
988
- id: currentCohortId,
989
- changes: {
990
- filters: {
991
- ...state.entities[currentCohortId].filters,
992
- [index]: filters
993
- },
994
- modified: true,
995
- modifiedDatetime: new Date().toISOString()
950
+ this.handleGreaterThan = (op)=>({
951
+ '>': {
952
+ [op.field]: op.operand
996
953
  }
997
954
  });
998
- },
999
- setCohortIndexFilters: (state, action)=>{
1000
- const currentCohortId = getCurrentCohortId(state);
1001
- if (!state.entities[currentCohortId]) {
1002
- console.error(`no cohort with id=${currentCohortId} defined`);
1003
- return;
1004
- }
1005
- cohortsAdapter.updateOne(state, {
1006
- id: currentCohortId,
1007
- changes: {
1008
- filters: action.payload.filters,
1009
- modified: true,
1010
- modifiedDatetime: new Date().toISOString()
955
+ this.handleGreaterThanOrEquals = (op)=>({
956
+ '>=': {
957
+ [op.field]: op.operand
1011
958
  }
1012
959
  });
1013
- },
1014
- // removes a filter to the cohort filter set at the given index
1015
- removeCohortFilter: (state, action)=>{
1016
- const { index, field } = action.payload;
1017
- const currentCohortId = getCurrentCohortId(state);
1018
- if (!state.entities[currentCohortId]) {
1019
- console.error(`no cohort with id=${currentCohortId} defined`);
1020
- return;
1021
- }
1022
- const filters = state.entities[currentCohortId]?.filters[index]?.root;
1023
- if (!filters) {
1024
- return;
1025
- }
1026
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1027
- const { [field]: _a, ...updated } = filters;
1028
- cohortsAdapter.updateOne(state, {
1029
- id: currentCohortId,
1030
- changes: {
1031
- filters: {
1032
- ...state.entities[currentCohortId]?.filters,
1033
- [index]: {
1034
- mode: state.entities[currentCohortId].filters[index].mode,
1035
- root: updated
1036
- }
1037
- },
1038
- modified: true,
1039
- modifiedDatetime: new Date().toISOString()
960
+ this.handleIncludes = (op)=>({
961
+ in: {
962
+ [op.field]: op.operands
1040
963
  }
1041
964
  });
1042
- },
1043
- duplicateCohort: (state)=>{
1044
- const currentCohortId = getCurrentCohortId(state);
1045
- const currentCohort = state.entities[currentCohortId];
1046
- const newName = generateUniqueName(Object.values(state.entities), currentCohort.name);
1047
- const duplicatedCohort = newCohort({
1048
- filters: {
1049
- ...currentCohort.filters
1050
- },
1051
- customName: newName
1052
- });
1053
- cohortsAdapter.addOne(state, {
1054
- ...duplicatedCohort,
1055
- counts: {
1056
- ...currentCohort.counts
965
+ this.handleExcludes = (op)=>({
966
+ exclude: {
967
+ [op.field]: op.operands
1057
968
  }
1058
969
  });
1059
- state.currentCohortId = duplicatedCohort.id;
1060
- },
1061
- // removes all filters from the cohort filter set at the given index
1062
- clearCohortFilters: (state, action)=>{
1063
- const { index } = action.payload;
1064
- const currentCohortId = getCurrentCohortId(state);
1065
- if (!state.entities[currentCohortId]) {
1066
- console.error(`no cohort with id=${currentCohortId} defined`);
1067
- return;
1068
- }
1069
- const filters = state.entities[currentCohortId]?.filters[index]?.root;
1070
- if (!filters) {
1071
- return;
1072
- }
1073
- cohortsAdapter.updateOne(state, {
1074
- id: currentCohortId,
1075
- changes: {
1076
- filters: {
1077
- ...state.entities[currentCohortId]?.filters,
1078
- [index]: {
1079
- mode: 'and',
1080
- root: {}
1081
- }
1082
- },
1083
- modified: true,
1084
- modifiedDatetime: new Date().toISOString()
970
+ this.handleExcludeIfAny = (op)=>({
971
+ excludeifany: {
972
+ [op.field]: op.operands
1085
973
  }
1086
974
  });
1087
- },
1088
- updateCohortCounts: (state, action)=>{
1089
- const currentCohortId = getCurrentCohortId(state);
1090
- const currentCohort = state.entities[currentCohortId];
1091
- cohortsAdapter.updateOne(state, {
1092
- id: currentCohortId,
1093
- changes: {
1094
- counts: {
1095
- ...currentCohort.counts,
1096
- ...action.payload
1097
- }
1098
- }
975
+ this.handleIntersection = (op)=>({
976
+ and: op.operands.map((x)=>convertFilterToGqlFilter(x))
1099
977
  });
1100
- },
1101
- updateCohortIndexCountById: (state, action)=>{
1102
- const { index, cohortId, counts } = action.payload;
1103
- const cohort = state.entities[cohortId];
1104
- cohortsAdapter.updateOne(state, {
1105
- id: cohortId,
1106
- changes: {
1107
- counts: {
1108
- ...cohort.counts,
1109
- ...{
1110
- [index]: counts
1111
- }
1112
- }
978
+ this.handleUnion = (op)=>({
979
+ or: op.operands.map((x)=>convertFilterToGqlFilter(x))
980
+ });
981
+ this.handleMissing = (op)=>({
982
+ is: {
983
+ [op.field]: 'MISSING'
1113
984
  }
1114
985
  });
1115
- },
1116
- setCurrentCohortId: (state, action)=>{
1117
- state.currentCohortId = action.payload;
1118
- },
1119
- /** @hidden */ setCohortList: (state, action)=>{
1120
- if (!action.payload) {
1121
- cohortsAdapter.removeMany(state, state.ids);
1122
- } else {
1123
- cohortsAdapter.upsertMany(state, [
1124
- ...action.payload
1125
- ]);
1126
- }
1127
- }
1128
- }
1129
- });
1130
- /**
1131
- * Returns the selectors for the cohorts EntityAdapter
1132
- * @param state - the CoreState
1133
- *
1134
- * @hidden
1135
- */ const cohortSelectors = cohortsAdapter.getSelectors((state)=>state.cohorts.cohortManager);
1136
- // Filter actions: addFilter, removeFilter, updateFilter
1137
- const { createNewCohort, updateCohortFilter, setCohortFilter, setCohortIndexFilters, duplicateCohort, removeCohortFilter, clearCohortFilters, removeCohort, setCurrentCohortId, updateCohortName, updateCohortCounts, updateCohortIndexCountById, setCohortList } = cohortManagerSlice.actions;
1138
- const cohortReducer = cohortManagerSlice.reducer;
1139
-
1140
- const initialState$2 = {};
1141
- const expandSlice$1 = toolkit.createSlice({
1142
- name: 'CohortBuilder/filterExpand',
1143
- initialState: initialState$2,
1144
- reducers: {
1145
- toggleCohortBuilderCategoryFilter: (state, action)=>{
1146
- return {
1147
- ...state,
1148
- [action.payload.index]: {
1149
- ...state[action.payload.index],
1150
- [action.payload.field]: action.payload.expanded
986
+ this.handleExists = (op)=>({
987
+ not: {
988
+ [op.field]: op?.operand ?? null
1151
989
  }
1152
- };
1153
- },
1154
- toggleCohortBuilderAllFilters: (state, action)=>{
1155
- return {
1156
- ...state,
1157
- [action.payload.index]: Object.keys(state[action.payload.index]).reduce((acc, k)=>{
1158
- acc[k] = action.payload.expand;
1159
- return acc;
1160
- }, {})
1161
- };
1162
- }
1163
- }
1164
- });
1165
- const cohortBuilderFiltersExpandedReducer = expandSlice$1.reducer;
1166
- const { toggleCohortBuilderCategoryFilter, toggleCohortBuilderAllFilters } = expandSlice$1.actions;
1167
- const selectCohortFilterExpanded = (state, index, field)=>state.cohorts.filtersExpanded?.[index]?.[field];
1168
- const selectAllCohortFiltersCollapsed = (state, index)=>index in state.cohorts.filtersExpanded ? Object.values(state.cohorts.filtersExpanded?.[index]).every((e)=>!e) : false;
1169
-
1170
- const initialState$1 = {};
1171
- const expandSlice = toolkit.createSlice({
1172
- name: 'CohortBuilder/filterCombineMode',
1173
- initialState: initialState$1,
1174
- reducers: {
1175
- setCohortFilterCombineMode: (state, action)=>{
990
+ });
991
+ this.handleNestedFilter = (op)=>{
992
+ const child = convertFilterToGqlFilter(op.operand);
1176
993
  return {
1177
- ...state,
1178
- [action.payload.index]: {
1179
- ...state[action.payload.index],
1180
- [action.payload.field]: action.payload.mode
994
+ nested: {
995
+ path: op.path,
996
+ ...child
1181
997
  }
1182
998
  };
1183
- }
999
+ };
1184
1000
  }
1185
- });
1186
- const cohortBuilderFiltersCombineModeReducer = expandSlice.reducer;
1187
- const { setCohortFilterCombineMode } = expandSlice.actions;
1188
- const selectCohortFilterCombineMode = (state, index, field)=>state.cohorts.filtersCombineMode?.[index]?.[field] ?? 'or';
1189
-
1190
- const initialState = {
1191
- shouldShareFilters: false,
1192
- sharedFiltersMap: {}
1001
+ }
1002
+ const convertFilterToGqlFilter = (filter)=>{
1003
+ const handler = new ToGqlHandler();
1004
+ return handleOperation(handler, filter);
1193
1005
  };
1194
- const cohortSharedFiltersSlice = toolkit.createSlice({
1195
- name: 'cohortSharedFilters',
1196
- initialState: initialState,
1197
- reducers: {
1198
- setShouldShareFilters: (state, action)=>{
1199
- state.shouldShareFilters = action.payload;
1200
- return state;
1201
- },
1202
- setSharedFilters: (state, action)=>{
1203
- state.sharedFiltersMap = action.payload;
1204
- }
1205
- }
1206
- });
1207
- const selectShouldShareFilters = (state)=>state.cohorts.sharedFilters.shouldShareFilters;
1208
- const selectSharedFilters = (state)=>state.cohorts.sharedFilters.sharedFiltersMap;
1209
- const selectSharedFiltersForFields = (state, field)=>state.cohorts.sharedFilters.sharedFiltersMap?.[field] ?? [
1210
- field
1211
- ];
1212
- const { setShouldShareFilters, setSharedFilters } = cohortSharedFiltersSlice.actions;
1213
- const cohortSharedFiltersReducer = cohortSharedFiltersSlice.reducer;
1214
-
1215
- const cohortReducers = toolkit.combineReducers({
1216
- filtersExpanded: cohortBuilderFiltersExpandedReducer,
1217
- filtersCombineMode: cohortBuilderFiltersCombineModeReducer,
1218
- sharedFilters: cohortSharedFiltersReducer,
1219
- cohortManager: cohortReducer
1220
- });
1221
-
1222
- const rootReducer = toolkit.combineReducers({
1223
- gen3Services: gen3ServicesReducer,
1224
- user: userReducer,
1225
- gen3Apps: gen3AppReducer,
1226
- drsHostnames: drsHostnamesReducer,
1227
- modals: modalReducer,
1228
- cohorts: cohortReducers,
1229
- activeWorkspace: activeWorkspaceReducer,
1230
- [guppyApiSliceReducerPath]: guppyApiReducer,
1231
- [userAuthApiReducerPath]: userAuthApiReducer
1232
- });
1233
-
1234
- const isOperationWithField = (operation)=>{
1235
- return operation?.field !== undefined;
1236
- };
1237
- const isOperatorWithFieldAndArrayOfOperands = (operation)=>{
1238
- if (typeof operation === 'object' && operation !== null && 'operands' in operation && Array.isArray(operation.operands) && 'field' in operation && typeof operation.field === 'string' // Assuming `field` should be a string
1239
- ) {
1240
- const { operator } = operation.operator;
1241
- return operator === 'in' || operator === 'exclude' || operator === 'excludeifany';
1242
- }
1243
- return false;
1244
- };
1245
- const extractFilterValue = (op)=>{
1246
- const valueExtractorHandler = new ValueExtractorHandler();
1247
- return handleOperation(valueExtractorHandler, op);
1248
- };
1249
- const extractEnumFilterValue = (op)=>{
1250
- const enumValueExtractorHandler = new EnumValueExtractorHandler();
1251
- const results = handleOperation(enumValueExtractorHandler, op);
1252
- return results ?? [];
1253
- };
1254
- const assertNever = (x)=>{
1255
- throw Error(`Exhaustive comparison did not handle: ${x}`);
1256
- };
1257
- const handleOperation = (handler, op)=>{
1258
- switch(op.operator){
1259
- case '=':
1260
- return handler.handleEquals(op);
1261
- case '!=':
1262
- return handler.handleNotEquals(op);
1263
- case '<':
1264
- return handler.handleLessThan(op);
1265
- case '<=':
1266
- return handler.handleLessThanOrEquals(op);
1267
- case '>':
1268
- return handler.handleGreaterThan(op);
1269
- case '>=':
1270
- return handler.handleGreaterThanOrEquals(op);
1271
- case 'and':
1272
- return handler.handleIntersection(op);
1273
- case 'or':
1274
- return handler.handleUnion(op);
1275
- case 'nested':
1276
- return handler.handleNestedFilter(op);
1277
- case 'in':
1278
- case 'includes':
1279
- return handler.handleIncludes(op);
1280
- case 'excludeifany':
1281
- return handler.handleExcludeIfAny(op);
1282
- case 'excludes':
1283
- return handler.handleExcludes(op);
1284
- case 'exists':
1285
- return handler.handleExists(op);
1286
- case 'missing':
1287
- return handler.handleMissing(op);
1288
- default:
1289
- return assertNever(op);
1290
- }
1291
- };
1292
- /**
1293
- * Return true if a FilterSet's root value is an empty object
1294
- * @param fs - FilterSet to test
1295
- */ const isFilterEmpty = (fs)=>lodash.isEqual({}, fs);
1296
- /**
1297
- * Type guard to check if an object is a GQLIntersection
1298
- * @param value - The value to check
1299
- * @returns True if the value is a GQLIntersection
1300
- */ const isGQLIntersection = (value)=>{
1301
- return typeof value === 'object' && value !== null && 'and' in value && Array.isArray(value.and);
1302
- };
1303
- /**
1304
- * Type guard to check if an object is a GQLIntersection
1305
- * @param value - The value to check
1306
- * @returns True if the value is a GQLIntersection
1307
- */ const isGQLUnion = (value)=>{
1308
- return typeof value === 'object' && value !== null && 'or' in value && Array.isArray(value.or);
1309
- };
1310
- class ToGqlHandler {
1311
- constructor(){
1312
- this.handleEquals = (op)=>({
1313
- '=': {
1314
- [op.field]: op.operand
1315
- }
1316
- });
1317
- this.handleNotEquals = (op)=>({
1318
- '!=': {
1319
- [op.field]: op.operand
1320
- }
1321
- });
1322
- this.handleLessThan = (op)=>({
1323
- '<': {
1324
- [op.field]: op.operand
1325
- }
1326
- });
1327
- this.handleLessThanOrEquals = (op)=>({
1328
- '<=': {
1329
- [op.field]: op.operand
1330
- }
1331
- });
1332
- this.handleGreaterThan = (op)=>({
1333
- '>': {
1334
- [op.field]: op.operand
1335
- }
1336
- });
1337
- this.handleGreaterThanOrEquals = (op)=>({
1338
- '>=': {
1339
- [op.field]: op.operand
1340
- }
1341
- });
1342
- this.handleIncludes = (op)=>({
1343
- in: {
1344
- [op.field]: op.operands
1345
- }
1346
- });
1347
- this.handleExcludes = (op)=>({
1348
- exclude: {
1349
- [op.field]: op.operands
1350
- }
1351
- });
1352
- this.handleExcludeIfAny = (op)=>({
1353
- excludeifany: {
1354
- [op.field]: op.operands
1355
- }
1356
- });
1357
- this.handleIntersection = (op)=>({
1358
- and: op.operands.map((x)=>convertFilterToGqlFilter(x))
1359
- });
1360
- this.handleUnion = (op)=>({
1361
- or: op.operands.map((x)=>convertFilterToGqlFilter(x))
1362
- });
1363
- this.handleMissing = (op)=>({
1364
- is: {
1365
- [op.field]: 'MISSING'
1366
- }
1367
- });
1368
- this.handleExists = (op)=>({
1369
- not: {
1370
- [op.field]: op?.operand ?? null
1371
- }
1372
- });
1373
- this.handleNestedFilter = (op)=>{
1374
- const child = convertFilterToGqlFilter(op.operand);
1375
- return {
1376
- nested: {
1377
- path: op.path,
1378
- ...child
1379
- }
1380
- };
1381
- };
1382
- }
1383
- }
1384
- const convertFilterToGqlFilter = (filter)=>{
1385
- const handler = new ToGqlHandler();
1386
- return handleOperation(handler, filter);
1387
- };
1388
- const convertFilterSetToGqlFilter = (fs, toplevelOp = 'and')=>{
1389
- const fsKeys = Object.keys(fs.root);
1390
- // if no keys return undefined
1391
- if (fsKeys.length === 0) return {
1392
- and: []
1393
- };
1394
- return toplevelOp === 'and' ? {
1395
- and: fsKeys.map((key)=>convertFilterToGqlFilter(fs.root[key]))
1396
- } : {
1397
- or: fsKeys.map((key)=>convertFilterToGqlFilter(fs.root[key]))
1398
- };
1399
- };
1400
- const handleGqlOperation = (handler, op)=>{
1401
- const operationKeys = Object.keys(op);
1402
- if (operationKeys.includes('=')) {
1403
- return handler.handleEquals(op);
1006
+ const convertFilterSetToGqlFilter = (fs, toplevelOp = 'and')=>{
1007
+ const fsKeys = Object.keys(fs.root);
1008
+ // if no keys return undefined
1009
+ if (fsKeys.length === 0) return {
1010
+ and: []
1011
+ };
1012
+ return toplevelOp === 'and' ? {
1013
+ and: fsKeys.map((key)=>convertFilterToGqlFilter(fs.root[key]))
1014
+ } : {
1015
+ or: fsKeys.map((key)=>convertFilterToGqlFilter(fs.root[key]))
1016
+ };
1017
+ };
1018
+ const handleGqlOperation = (handler, op)=>{
1019
+ const operationKeys = Object.keys(op);
1020
+ if (operationKeys.includes('=')) {
1021
+ return handler.handleEquals(op);
1404
1022
  }
1405
1023
  if (operationKeys.includes('!=')) {
1406
1024
  return handler.handleNotEquals(op);
@@ -1677,9 +1295,17 @@ const isUnion = (value)=>{
1677
1295
  const isIntersection = (value)=>{
1678
1296
  return typeof value === 'object' && value !== null && value.operator === 'and' && Array.isArray(value.operands);
1679
1297
  };
1298
+ /**
1299
+ * Type guard for Union or Intersection
1300
+ * @param o - operator to check
1301
+ * @category Filters
1302
+ */ const isIntersectionOrUnion = (o)=>o.operator === 'and' || o.operator === 'or';
1680
1303
  const isOperandsType = (operation)=>{
1681
1304
  return operation?.operands !== undefined;
1682
1305
  };
1306
+ const isNestedFilter = (operation)=>{
1307
+ return operation.operator === 'nested';
1308
+ };
1683
1309
  const isIndexedFilterSetEmpty = (filters)=>Object.values(filters).every((filterSet)=>Object.keys(filterSet).length === 0);
1684
1310
  const EmptyFilterSet = {
1685
1311
  mode: 'and',
@@ -1749,6 +1375,437 @@ const trimFirstFieldNameToTitle = (fieldName, trim = false)=>{
1749
1375
  ];
1750
1376
  };
1751
1377
 
1378
+ const defaultCohortNameGenerator = ()=>`Custom cohort ${new Date().toLocaleString('en-CA', {
1379
+ timeZone: 'America/Chicago',
1380
+ hour12: false
1381
+ }).replace(',', '')}`;
1382
+ const isNameUnique = (entities, name, excludeId)=>{
1383
+ const trimmedName = name.trim();
1384
+ if (!trimmedName) return false;
1385
+ return !entities.some((cohort)=>cohort && cohort.id !== excludeId && cohort.name.trim().toLowerCase() === trimmedName.toLowerCase());
1386
+ };
1387
+ const generateUniqueName = (entities, baseName)=>{
1388
+ const trimmedBaseName = baseName.trim();
1389
+ // If base name is unique, use it
1390
+ if (isNameUnique(entities, trimmedBaseName)) {
1391
+ return trimmedBaseName;
1392
+ }
1393
+ // Find a unique name by appending numbers
1394
+ let counter = 1;
1395
+ let uniqueName;
1396
+ do {
1397
+ uniqueName = `${trimmedBaseName} (${counter})`;
1398
+ counter++;
1399
+ }while (!isNameUnique(entities, uniqueName))
1400
+ return uniqueName;
1401
+ };
1402
+ /**
1403
+ * This function takes a FilterSet object and a prefix string as input.
1404
+ * It filters the root property of the FilterSet object and returns a
1405
+ * new FilterSet object that only contains filters with field names
1406
+ * that start with the specified prefix.
1407
+ *
1408
+ * @param fs - The FilterSet object to filter
1409
+ * @param prefix - The prefix to filter by
1410
+ * @returns - A new FilterSet object that only contains filters with field names that start with the specified prefix
1411
+ * @category Filters
1412
+ */ const extractFiltersWithPrefixFromFilterSet = (fs, prefix)=>{
1413
+ if (fs === undefined || fs.root === undefined) {
1414
+ return {
1415
+ mode: 'and',
1416
+ root: {}
1417
+ };
1418
+ }
1419
+ return Object.values(fs.root).reduce((acc, filter)=>{
1420
+ if (isIntersectionOrUnion(filter) || isNestedFilter(filter)) return acc;
1421
+ if (filter.field.startsWith(prefix)) {
1422
+ acc.root[filter.field] = filter;
1423
+ }
1424
+ return acc;
1425
+ }, {
1426
+ mode: 'and',
1427
+ root: {}
1428
+ });
1429
+ };
1430
+
1431
+ /**
1432
+ * Cohorts in Gen3 are defined as a set of filters for each index in the data.
1433
+ * This means one cohort id defined for all "tabs" in CohortBuilder (explorer)
1434
+ * Switching a cohort id means all the cohorts for the index are changed.
1435
+ */ const DEFAULT_COHORT_NAME = 'Cohort';
1436
+ const newCohort = ({ filters = {}, customName })=>{
1437
+ const ts = new Date().toISOString();
1438
+ const newName = customName ?? defaultCohortNameGenerator();
1439
+ const newId = createCohortId();
1440
+ return {
1441
+ name: newName,
1442
+ id: newId,
1443
+ filters: filters ?? {},
1444
+ modified: false,
1445
+ saved: false,
1446
+ createdDatetime: ts,
1447
+ modifiedDatetime: ts,
1448
+ counts: {}
1449
+ };
1450
+ };
1451
+ const nanoid = nanoid$1.customAlphabet('1234567890abcdef', 16);
1452
+ const createCohortId = ()=>nanoid();
1453
+ const cohortsAdapter = toolkit.createEntityAdapter({
1454
+ sortComparer: (a, b)=>{
1455
+ if (a.modifiedDatetime <= b.modifiedDatetime) return 1;
1456
+ else return -1;
1457
+ },
1458
+ selectId: (cohort)=>cohort.id
1459
+ });
1460
+ // Create an initial unsaved cohort
1461
+ const initialCohort = newCohort({
1462
+ customName: DEFAULT_COHORT_NAME
1463
+ });
1464
+ const emptyInitialState = cohortsAdapter.getInitialState({
1465
+ currentCohortId: initialCohort.id,
1466
+ message: undefined
1467
+ });
1468
+ // Set the initial cohort in the adapter state
1469
+ const initialState$3 = cohortsAdapter.setOne(emptyInitialState, initialCohort);
1470
+ const getCurrentCohortId = (state)=>state.currentCohortId;
1471
+ /**
1472
+ * Redux slice for cohort filters
1473
+ */ const cohortManagerSlice = toolkit.createSlice({
1474
+ name: 'cohort',
1475
+ initialState: initialState$3,
1476
+ reducers: {
1477
+ createNewCohort: (state, action)=>{
1478
+ const baseName = action.payload.name || `Cohort`;
1479
+ const uniqueName = generateUniqueName(Object.values(state.entities), baseName);
1480
+ const cohort = newCohort({
1481
+ filters: action.payload.filters,
1482
+ customName: uniqueName
1483
+ });
1484
+ cohortsAdapter.addOne(state, cohort);
1485
+ state.currentCohortId = cohort.id;
1486
+ },
1487
+ updateCohortName: (state, action)=>{
1488
+ const { id, name } = action.payload;
1489
+ cohortsAdapter.updateOne(state, {
1490
+ id: id,
1491
+ changes: {
1492
+ name: name,
1493
+ modified: true,
1494
+ modifiedDatetime: new Date().toISOString()
1495
+ }
1496
+ });
1497
+ },
1498
+ removeCohort: (state, action)=>{
1499
+ const { id: cohortId } = action.payload;
1500
+ const removedCohortName = state.entities[cohortId].name;
1501
+ const totalCohorts = Object.keys(state.entities).length;
1502
+ if (totalCohorts <= 1) {
1503
+ cohortsAdapter.removeAll(state);
1504
+ const defaultCohort = newCohort({
1505
+ filters: {},
1506
+ customName: DEFAULT_COHORT_NAME
1507
+ });
1508
+ cohortsAdapter.addOne(state, defaultCohort);
1509
+ state.currentCohortId = defaultCohort.id;
1510
+ if (action?.payload.shouldShowMessage) {
1511
+ state.message = [
1512
+ `deleteCohort|${removedCohortName}|${state.currentCohortId}`
1513
+ ];
1514
+ }
1515
+ return;
1516
+ }
1517
+ cohortsAdapter.removeOne(state, cohortId);
1518
+ // deleted the current cohort so set to the most recent cohort
1519
+ if (state.currentCohortId === cohortId) {
1520
+ const remainingIds = Object.keys(state.entities);
1521
+ state.currentCohortId = remainingIds[0];
1522
+ }
1523
+ if (action?.payload.shouldShowMessage) {
1524
+ state.message = [
1525
+ `deleteCohort|${removedCohortName}|${state.currentCohortId}`
1526
+ ];
1527
+ }
1528
+ },
1529
+ // adds a filter to the cohort filter set at the given index
1530
+ updateCohortFilter: (state, action)=>{
1531
+ const { index, field, filter } = action.payload;
1532
+ const currentCohortId = getCurrentCohortId(state);
1533
+ if (!state.entities[currentCohortId]) {
1534
+ return;
1535
+ }
1536
+ cohortsAdapter.updateOne(state, {
1537
+ id: currentCohortId,
1538
+ changes: {
1539
+ filters: {
1540
+ ...state.entities[currentCohortId].filters,
1541
+ [index]: {
1542
+ mode: state.entities[currentCohortId]?.filters[index]?.mode ?? 'and',
1543
+ root: {
1544
+ ...state.entities[currentCohortId]?.filters[index]?.root ?? {},
1545
+ [field]: filter
1546
+ }
1547
+ }
1548
+ },
1549
+ modified: true,
1550
+ modifiedDatetime: new Date().toISOString()
1551
+ }
1552
+ });
1553
+ },
1554
+ setCohortFilter: (state, action)=>{
1555
+ const { index, filters } = action.payload;
1556
+ const currentCohortId = getCurrentCohortId(state);
1557
+ if (!state.entities[currentCohortId]) {
1558
+ console.error(`no cohort with id=${currentCohortId} defined`);
1559
+ return;
1560
+ }
1561
+ cohortsAdapter.updateOne(state, {
1562
+ id: currentCohortId,
1563
+ changes: {
1564
+ filters: {
1565
+ ...state.entities[currentCohortId].filters,
1566
+ [index]: filters
1567
+ },
1568
+ modified: true,
1569
+ modifiedDatetime: new Date().toISOString()
1570
+ }
1571
+ });
1572
+ },
1573
+ setCohortIndexFilters: (state, action)=>{
1574
+ const currentCohortId = getCurrentCohortId(state);
1575
+ if (!state.entities[currentCohortId]) {
1576
+ console.error(`no cohort with id=${currentCohortId} defined`);
1577
+ return;
1578
+ }
1579
+ cohortsAdapter.updateOne(state, {
1580
+ id: currentCohortId,
1581
+ changes: {
1582
+ filters: action.payload.filters,
1583
+ modified: true,
1584
+ modifiedDatetime: new Date().toISOString()
1585
+ }
1586
+ });
1587
+ },
1588
+ // removes a filter to the cohort filter set at the given index
1589
+ removeCohortFilter: (state, action)=>{
1590
+ const { index, field } = action.payload;
1591
+ const currentCohortId = getCurrentCohortId(state);
1592
+ if (!state.entities[currentCohortId]) {
1593
+ console.error(`no cohort with id=${currentCohortId} defined`);
1594
+ return;
1595
+ }
1596
+ const filters = state.entities[currentCohortId]?.filters[index]?.root;
1597
+ if (!filters) {
1598
+ return;
1599
+ }
1600
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1601
+ const { [field]: _a, ...updated } = filters;
1602
+ cohortsAdapter.updateOne(state, {
1603
+ id: currentCohortId,
1604
+ changes: {
1605
+ filters: {
1606
+ ...state.entities[currentCohortId]?.filters,
1607
+ [index]: {
1608
+ mode: state.entities[currentCohortId].filters[index].mode,
1609
+ root: updated
1610
+ }
1611
+ },
1612
+ modified: true,
1613
+ modifiedDatetime: new Date().toISOString()
1614
+ }
1615
+ });
1616
+ },
1617
+ duplicateCohort: (state)=>{
1618
+ const currentCohortId = getCurrentCohortId(state);
1619
+ const currentCohort = state.entities[currentCohortId];
1620
+ const newName = generateUniqueName(Object.values(state.entities), currentCohort.name);
1621
+ const duplicatedCohort = newCohort({
1622
+ filters: {
1623
+ ...currentCohort.filters
1624
+ },
1625
+ customName: newName
1626
+ });
1627
+ cohortsAdapter.addOne(state, {
1628
+ ...duplicatedCohort,
1629
+ counts: {
1630
+ ...currentCohort.counts
1631
+ }
1632
+ });
1633
+ state.currentCohortId = duplicatedCohort.id;
1634
+ },
1635
+ // removes all filters from the cohort filter set at the given index
1636
+ clearCohortFilters: (state, action)=>{
1637
+ const { index } = action.payload;
1638
+ const currentCohortId = getCurrentCohortId(state);
1639
+ if (!state.entities[currentCohortId]) {
1640
+ console.error(`no cohort with id=${currentCohortId} defined`);
1641
+ return;
1642
+ }
1643
+ const filters = state.entities[currentCohortId]?.filters[index]?.root;
1644
+ if (!filters) {
1645
+ return;
1646
+ }
1647
+ cohortsAdapter.updateOne(state, {
1648
+ id: currentCohortId,
1649
+ changes: {
1650
+ filters: {
1651
+ ...state.entities[currentCohortId]?.filters,
1652
+ [index]: {
1653
+ mode: 'and',
1654
+ root: {}
1655
+ }
1656
+ },
1657
+ modified: true,
1658
+ modifiedDatetime: new Date().toISOString()
1659
+ }
1660
+ });
1661
+ },
1662
+ updateCohortCounts: (state, action)=>{
1663
+ const currentCohortId = getCurrentCohortId(state);
1664
+ const currentCohort = state.entities[currentCohortId];
1665
+ cohortsAdapter.updateOne(state, {
1666
+ id: currentCohortId,
1667
+ changes: {
1668
+ counts: {
1669
+ ...currentCohort.counts,
1670
+ ...action.payload
1671
+ }
1672
+ }
1673
+ });
1674
+ },
1675
+ updateCohortIndexCountById: (state, action)=>{
1676
+ const { index, cohortId, counts } = action.payload;
1677
+ const cohort = state.entities[cohortId];
1678
+ cohortsAdapter.updateOne(state, {
1679
+ id: cohortId,
1680
+ changes: {
1681
+ counts: {
1682
+ ...cohort.counts,
1683
+ ...{
1684
+ [index]: counts
1685
+ }
1686
+ }
1687
+ }
1688
+ });
1689
+ },
1690
+ setCurrentCohortId: (state, action)=>{
1691
+ state.currentCohortId = action.payload;
1692
+ },
1693
+ /** @hidden */ setCohortList: (state, action)=>{
1694
+ if (!action.payload) {
1695
+ cohortsAdapter.removeMany(state, state.ids);
1696
+ } else {
1697
+ cohortsAdapter.upsertMany(state, [
1698
+ ...action.payload
1699
+ ]);
1700
+ }
1701
+ }
1702
+ }
1703
+ });
1704
+ /**
1705
+ * Returns the selectors for the cohorts EntityAdapter
1706
+ * @param state - the CoreState
1707
+ *
1708
+ * @hidden
1709
+ */ const cohortSelectors = cohortsAdapter.getSelectors((state)=>state.cohorts.cohortManager);
1710
+ // Filter actions: addFilter, removeFilter, updateFilter
1711
+ const { createNewCohort, updateCohortFilter, setCohortFilter, setCohortIndexFilters, duplicateCohort, removeCohortFilter, clearCohortFilters, removeCohort, setCurrentCohortId, updateCohortName, updateCohortCounts, updateCohortIndexCountById, setCohortList } = cohortManagerSlice.actions;
1712
+ const cohortReducer = cohortManagerSlice.reducer;
1713
+
1714
+ const initialState$2 = {};
1715
+ const expandSlice$1 = toolkit.createSlice({
1716
+ name: 'CohortBuilder/filterExpand',
1717
+ initialState: initialState$2,
1718
+ reducers: {
1719
+ toggleCohortBuilderCategoryFilter: (state, action)=>{
1720
+ return {
1721
+ ...state,
1722
+ [action.payload.index]: {
1723
+ ...state[action.payload.index],
1724
+ [action.payload.field]: action.payload.expanded
1725
+ }
1726
+ };
1727
+ },
1728
+ toggleCohortBuilderAllFilters: (state, action)=>{
1729
+ return {
1730
+ ...state,
1731
+ [action.payload.index]: Object.keys(state[action.payload.index]).reduce((acc, k)=>{
1732
+ acc[k] = action.payload.expand;
1733
+ return acc;
1734
+ }, {})
1735
+ };
1736
+ }
1737
+ }
1738
+ });
1739
+ const cohortBuilderFiltersExpandedReducer = expandSlice$1.reducer;
1740
+ const { toggleCohortBuilderCategoryFilter, toggleCohortBuilderAllFilters } = expandSlice$1.actions;
1741
+ const selectCohortFilterExpanded = (state, index, field)=>state.cohorts.filtersExpanded?.[index]?.[field];
1742
+ const selectAllCohortFiltersCollapsed = (state, index)=>index in state.cohorts.filtersExpanded ? Object.values(state.cohorts.filtersExpanded?.[index]).every((e)=>!e) : false;
1743
+
1744
+ const initialState$1 = {};
1745
+ const expandSlice = toolkit.createSlice({
1746
+ name: 'CohortBuilder/filterCombineMode',
1747
+ initialState: initialState$1,
1748
+ reducers: {
1749
+ setCohortFilterCombineMode: (state, action)=>{
1750
+ return {
1751
+ ...state,
1752
+ [action.payload.index]: {
1753
+ ...state[action.payload.index],
1754
+ [action.payload.field]: action.payload.mode
1755
+ }
1756
+ };
1757
+ }
1758
+ }
1759
+ });
1760
+ const cohortBuilderFiltersCombineModeReducer = expandSlice.reducer;
1761
+ const { setCohortFilterCombineMode } = expandSlice.actions;
1762
+ const selectCohortFilterCombineMode = (state, index, field)=>state.cohorts.filtersCombineMode?.[index]?.[field] ?? 'or';
1763
+
1764
+ const initialState = {
1765
+ shouldShareFilters: false,
1766
+ sharedFiltersMap: {}
1767
+ };
1768
+ const cohortSharedFiltersSlice = toolkit.createSlice({
1769
+ name: 'cohortSharedFilters',
1770
+ initialState: initialState,
1771
+ reducers: {
1772
+ setShouldShareFilters: (state, action)=>{
1773
+ state.shouldShareFilters = action.payload;
1774
+ return state;
1775
+ },
1776
+ setSharedFilters: (state, action)=>{
1777
+ state.sharedFiltersMap = action.payload;
1778
+ }
1779
+ }
1780
+ });
1781
+ const selectShouldShareFilters = (state)=>state.cohorts.sharedFilters.shouldShareFilters;
1782
+ const selectSharedFilters = (state)=>state.cohorts.sharedFilters.sharedFiltersMap;
1783
+ const selectSharedFiltersForFields = (state, field)=>state.cohorts.sharedFilters.sharedFiltersMap?.[field] ?? [
1784
+ field
1785
+ ];
1786
+ const { setShouldShareFilters, setSharedFilters } = cohortSharedFiltersSlice.actions;
1787
+ const cohortSharedFiltersReducer = cohortSharedFiltersSlice.reducer;
1788
+
1789
+ const cohortReducers = toolkit.combineReducers({
1790
+ filtersExpanded: cohortBuilderFiltersExpandedReducer,
1791
+ filtersCombineMode: cohortBuilderFiltersCombineModeReducer,
1792
+ sharedFilters: cohortSharedFiltersReducer,
1793
+ cohortManager: cohortReducer
1794
+ });
1795
+
1796
+ const rootReducer = toolkit.combineReducers({
1797
+ gen3Services: gen3ServicesReducer,
1798
+ user: userReducer,
1799
+ gen3Apps: gen3AppReducer,
1800
+ drsHostnames: drsHostnamesReducer,
1801
+ modals: modalReducer,
1802
+ cohorts: cohortReducers,
1803
+ activeWorkspace: activeWorkspaceReducer,
1804
+ [guppyApiSliceReducerPath]: guppyApiReducer,
1805
+ [userAuthApiReducerPath]: userAuthApiReducer,
1806
+ [cartReducerPath]: cartReducer
1807
+ });
1808
+
1752
1809
  /**
1753
1810
  * Flattens a deep nested JSON object skipping
1754
1811
  * the first level to avoid potentially flattening
@@ -2574,7 +2631,8 @@ const persistConfig = {
2574
2631
  storage,
2575
2632
  whitelist: [
2576
2633
  'cohorts',
2577
- 'activeWorkspace'
2634
+ 'activeWorkspace',
2635
+ 'cart'
2578
2636
  ]
2579
2637
  };
2580
2638
  const persistedReducer = reduxPersist.persistReducer(persistConfig, rootReducer);
@@ -5759,6 +5817,7 @@ const isWorkspaceActive = (status)=>status === WorkspaceStatus.Running || status
5759
5817
  const isWorkspaceRunningOrStopping = (status)=>status === WorkspaceStatus.Running || status === WorkspaceStatus.Terminating;
5760
5818
 
5761
5819
  exports.Accessibility = Accessibility;
5820
+ exports.CART_LIMIT = CART_LIMIT;
5762
5821
  exports.CohortStorage = CohortStorage;
5763
5822
  exports.CoreProvider = CoreProvider;
5764
5823
  exports.DAYS_IN_YEAR = DAYS_IN_YEAR;
@@ -5792,6 +5851,7 @@ exports.RequestedWorkspaceStatus = RequestedWorkspaceStatus;
5792
5851
  exports.ToGqlHandler = ToGqlHandler;
5793
5852
  exports.ValueExtractorHandler = ValueExtractorHandler;
5794
5853
  exports.WorkspaceStatus = WorkspaceStatus;
5854
+ exports.addItemsToCart = addItemsToCart;
5795
5855
  exports.ageDisplay = ageDisplay;
5796
5856
  exports.appendFilterToOperation = appendFilterToOperation;
5797
5857
  exports.buildGetAggregationQuery = buildGetAggregationQuery;
@@ -5801,6 +5861,8 @@ exports.buildNestedGQLFilter = buildNestedGQLFilter;
5801
5861
  exports.calculatePercentageAsNumber = calculatePercentageAsNumber;
5802
5862
  exports.calculatePercentageAsString = calculatePercentageAsString;
5803
5863
  exports.capitalize = capitalize;
5864
+ exports.cartReducer = cartReducer;
5865
+ exports.cartReducerPath = cartReducerPath;
5804
5866
  exports.clearActiveWorkspaceId = clearActiveWorkspaceId;
5805
5867
  exports.clearCohortFilters = clearCohortFilters;
5806
5868
  exports.cohortReducer = cohortReducer;
@@ -5828,6 +5890,7 @@ exports.extractEnumFilterValue = extractEnumFilterValue;
5828
5890
  exports.extractFieldNameFromFullFieldName = extractFieldNameFromFullFieldName;
5829
5891
  exports.extractFileDatasetsInRecords = extractFileDatasetsInRecords;
5830
5892
  exports.extractFilterValue = extractFilterValue;
5893
+ exports.extractFiltersWithPrefixFromFilterSet = extractFiltersWithPrefixFromFilterSet;
5831
5894
  exports.extractIndexAndFieldNameFromFullFieldName = extractIndexAndFieldNameFromFullFieldName;
5832
5895
  exports.extractIndexFromDataLibraryCohort = extractIndexFromDataLibraryCohort;
5833
5896
  exports.extractIndexFromFullFieldName = extractIndexFromFullFieldName;
@@ -5885,10 +5948,12 @@ exports.isHistogramRangeData = isHistogramRangeData;
5885
5948
  exports.isHttpStatusError = isHttpStatusError;
5886
5949
  exports.isIndexedFilterSetEmpty = isIndexedFilterSetEmpty;
5887
5950
  exports.isIntersection = isIntersection;
5951
+ exports.isIntersectionOrUnion = isIntersectionOrUnion;
5888
5952
  exports.isJSONObject = isJSONObject;
5889
5953
  exports.isJSONValue = isJSONValue;
5890
5954
  exports.isJSONValueArray = isJSONValueArray;
5891
5955
  exports.isNameUnique = isNameUnique;
5956
+ exports.isNestedFilter = isNestedFilter;
5892
5957
  exports.isNotDefined = isNotDefined;
5893
5958
  exports.isObject = isObject;
5894
5959
  exports.isOperandsType = isOperandsType;
@@ -5918,6 +5983,7 @@ exports.rawDataQueryStrForEachField = rawDataQueryStrForEachField;
5918
5983
  exports.registerDefaultRemoteSupport = registerDefaultRemoteSupport;
5919
5984
  exports.removeCohort = removeCohort;
5920
5985
  exports.removeCohortFilter = removeCohortFilter;
5986
+ exports.removeItemsFromCart = removeItemsFromCart;
5921
5987
  exports.requestorApi = requestorApi;
5922
5988
  exports.resetUserState = resetUserState;
5923
5989
  exports.resourcePathFromProjectID = resourcePathFromProjectID;
@@ -5931,6 +5997,10 @@ exports.selectAvailableCohortByName = selectAvailableCohortByName;
5931
5997
  exports.selectAvailableCohorts = selectAvailableCohorts;
5932
5998
  exports.selectCSRFToken = selectCSRFToken;
5933
5999
  exports.selectCSRFTokenData = selectCSRFTokenData;
6000
+ exports.selectCart = selectCart;
6001
+ exports.selectCartCount = selectCartCount;
6002
+ exports.selectCartItem = selectCartItem;
6003
+ exports.selectCartItems = selectCartItems;
5934
6004
  exports.selectCohortById = selectCohortById;
5935
6005
  exports.selectCohortFilterCombineMode = selectCohortFilterCombineMode;
5936
6006
  exports.selectCohortFilterExpanded = selectCohortFilterExpanded;