@databiosphere/findable-ui 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/Filter/components/FilterLabel/filterLabel.styles.js +1 -2
- package/lib/components/Filter/components/Filters/filters.js +11 -1
- package/lib/components/Filter/components/Filters/filters.styles.d.ts +5 -0
- package/lib/components/Filter/components/Filters/filters.styles.js +14 -1
- package/lib/components/Table/table.js +1 -1
- package/lib/components/common/AnchorLink/anchorLink.styles.js +1 -0
- package/lib/config/entities.d.ts +10 -3
- package/lib/hooks/useEntityList.js +1 -1
- package/lib/hooks/useSessionTimeout.js +9 -2
- package/lib/providers/exploreState/entities.d.ts +19 -0
- package/lib/providers/exploreState/entities.js +2 -0
- package/lib/providers/exploreState/initializer/constants.d.ts +5 -0
- package/lib/providers/exploreState/initializer/constants.js +32 -0
- package/lib/providers/exploreState/initializer/utils.d.ts +12 -0
- package/lib/providers/exploreState/initializer/utils.js +116 -0
- package/lib/providers/exploreState/utils.d.ts +37 -22
- package/lib/providers/exploreState/utils.js +64 -65
- package/lib/providers/exploreState.d.ts +4 -21
- package/lib/providers/exploreState.js +23 -22
- package/lib/views/ExploreView/exploreView.js +6 -6
- package/package.json +1 -1
- package/src/components/Filter/components/FilterLabel/filterLabel.styles.ts +1 -2
- package/src/components/Filter/components/Filters/filters.styles.ts +18 -0
- package/src/components/Filter/components/Filters/filters.tsx +22 -3
- package/src/components/Table/table.tsx +1 -1
- package/src/components/common/AnchorLink/anchorLink.styles.ts +1 -0
- package/src/config/entities.ts +11 -3
- package/src/hooks/useEntityList.ts +1 -1
- package/src/hooks/useSessionTimeout.ts +9 -3
- package/src/providers/exploreState/entities.ts +32 -0
- package/src/providers/exploreState/{constants.ts → initializer/constants.ts} +8 -3
- package/src/providers/exploreState/initializer/utils.ts +183 -0
- package/src/providers/exploreState/utils.ts +103 -92
- package/src/providers/exploreState.tsx +50 -81
- package/src/views/ExploreView/exploreView.tsx +8 -8
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { ColumnSort } from "@tanstack/react-table";
|
|
2
1
|
import React, { Dispatch, ReactNode } from "react";
|
|
3
2
|
import { AzulSearchIndex } from "../apis/azul/common/entities";
|
|
4
3
|
import { SelectCategory, SelectedFilter } from "../common/entities";
|
|
5
|
-
import {
|
|
4
|
+
import { CategoryGroup, SiteConfig } from "../config/entities";
|
|
5
|
+
import { EntityPageStateMapper, EntityStateByCategoryGroupConfigKey } from "./exploreState/entities";
|
|
6
6
|
import { PaginateTablePayload, ProcessExploreResponsePayload, ProcessRelatedResponsePayload, ResetExploreResponsePayload, ToggleEntityViewPayload, UpdateColumnVisibilityPayload, UpdateFilterPayload, UpdateSortingPayload } from "./exploreState/payloads/entities";
|
|
7
7
|
export declare type CatalogState = string | undefined;
|
|
8
8
|
/**
|
|
@@ -19,32 +19,15 @@ export interface ExploreContext {
|
|
|
19
19
|
config: SiteConfig;
|
|
20
20
|
entityList: string;
|
|
21
21
|
}
|
|
22
|
-
/**
|
|
23
|
-
* State for each entity.
|
|
24
|
-
*/
|
|
25
|
-
export interface EntityPageState {
|
|
26
|
-
categoryConfigs?: CategoryConfig[];
|
|
27
|
-
categoryGroupConfigs?: CategoryGroupConfig[];
|
|
28
|
-
categoryViews: SelectCategory[];
|
|
29
|
-
columnsVisibility: Record<string, boolean>;
|
|
30
|
-
filterCount: number;
|
|
31
|
-
filterState: SelectedFilter[];
|
|
32
|
-
sorting: ColumnSort[];
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* State for all entities.
|
|
36
|
-
*/
|
|
37
|
-
export interface EntityPageStateMapper {
|
|
38
|
-
[key: EntityPath]: EntityPageState;
|
|
39
|
-
}
|
|
40
22
|
/**
|
|
41
23
|
* Explore state.
|
|
42
24
|
*/
|
|
43
25
|
export declare type ExploreState = {
|
|
44
26
|
catalogState: CatalogState;
|
|
45
|
-
|
|
27
|
+
categoryGroups?: CategoryGroup[];
|
|
46
28
|
categoryViews: SelectCategory[];
|
|
47
29
|
entityPageState: EntityPageStateMapper;
|
|
30
|
+
entityStateByCategoryGroupConfigKey: EntityStateByCategoryGroupConfigKey;
|
|
48
31
|
featureFlagState: FeatureFlagState;
|
|
49
32
|
filterCount: number;
|
|
50
33
|
filterState: SelectedFilter[];
|
|
@@ -29,8 +29,9 @@ const useAuthentication_1 = require("../hooks/useAuthentication/useAuthenticatio
|
|
|
29
29
|
const useCategoryFilter_1 = require("../hooks/useCategoryFilter");
|
|
30
30
|
const useConfig_1 = require("../hooks/useConfig");
|
|
31
31
|
const useURLFilterParams_1 = require("../hooks/useURLFilterParams");
|
|
32
|
-
const constants_1 = require("./exploreState/constants");
|
|
33
|
-
const utils_1 = require("./exploreState/utils");
|
|
32
|
+
const constants_1 = require("./exploreState/initializer/constants");
|
|
33
|
+
const utils_1 = require("./exploreState/initializer/utils");
|
|
34
|
+
const utils_2 = require("./exploreState/utils");
|
|
34
35
|
/**
|
|
35
36
|
* Entity view.
|
|
36
37
|
*/
|
|
@@ -66,11 +67,11 @@ function ExploreStateProvider({ children, entityListType, }) {
|
|
|
66
67
|
const { decodedCatalogParam, decodedFeatureFlagParam, decodedFilterParam } = (0, useURLFilterParams_1.useURLFilterParams)();
|
|
67
68
|
const { isEnabled: isAuthEnabled, token } = (0, useAuthentication_1.useAuthentication)();
|
|
68
69
|
const entityList = entityListType || defaultEntityListType;
|
|
69
|
-
const [
|
|
70
|
+
const [initializerArg] = (0, react_1.useState)(() => (0, utils_1.initReducerArguments)(config, entityList, decodedFilterParam, decodedCatalogParam, decodedFeatureFlagParam));
|
|
70
71
|
const [exploreState, exploreDispatch] = (0, react_1.useReducer)((s, a) => exploreReducer(s, a, {
|
|
71
72
|
config,
|
|
72
73
|
entityList,
|
|
73
|
-
}),
|
|
74
|
+
}), initializerArg);
|
|
74
75
|
// does this help? https://hswolff.com/blog/how-to-usecontext-with-usereducer/
|
|
75
76
|
const exploreContextValue = (0, react_1.useMemo)(() => {
|
|
76
77
|
return { exploreDispatch, exploreState };
|
|
@@ -119,7 +120,11 @@ function exploreReducer(state, action, exploreContext) {
|
|
|
119
120
|
* Clear all filters
|
|
120
121
|
**/
|
|
121
122
|
case ExploreActionKind.ClearFilters: {
|
|
122
|
-
|
|
123
|
+
const filterCount = 0;
|
|
124
|
+
const filterState = [];
|
|
125
|
+
(0, utils_2.updateEntityStateByCategoryGroupConfigKey)(state, { filterState });
|
|
126
|
+
return Object.assign(Object.assign({}, state), { filterCount,
|
|
127
|
+
filterState, paginationState: (0, utils_2.resetPage)(state.paginationState) });
|
|
123
128
|
}
|
|
124
129
|
/**
|
|
125
130
|
* Paginate table
|
|
@@ -140,12 +145,13 @@ function exploreReducer(state, action, exploreContext) {
|
|
|
140
145
|
* Process explore response
|
|
141
146
|
**/
|
|
142
147
|
case ExploreActionKind.ProcessExploreResponse: {
|
|
143
|
-
const { entityPageState, tabValue } = state;
|
|
144
|
-
const { categoryConfigs, categoryViews, filterState } = entityPageState[tabValue];
|
|
145
148
|
const nextCategoryViews = payload.selectCategories
|
|
146
|
-
? (0, useCategoryFilter_1.buildCategoryViews)(payload.selectCategories,
|
|
147
|
-
:
|
|
148
|
-
|
|
149
|
+
? (0, useCategoryFilter_1.buildCategoryViews)(payload.selectCategories, (0, utils_2.getEntityCategoryConfigs)(state), state.filterState)
|
|
150
|
+
: state.categoryViews;
|
|
151
|
+
(0, utils_2.updateEntityStateByCategoryGroupConfigKey)(state, {
|
|
152
|
+
categoryViews: nextCategoryViews,
|
|
153
|
+
});
|
|
154
|
+
return Object.assign(Object.assign({}, state), { categoryViews: nextCategoryViews, listItems: payload.loading ? [] : payload.listItems, loading: payload.loading, paginationState: Object.assign(Object.assign({}, state.paginationState), payload.paginationResponse) });
|
|
149
155
|
}
|
|
150
156
|
/**
|
|
151
157
|
* Process related response
|
|
@@ -163,7 +169,7 @@ function exploreReducer(state, action, exploreContext) {
|
|
|
163
169
|
* Reset the current state to the initial
|
|
164
170
|
*/
|
|
165
171
|
case ExploreActionKind.ResetState: {
|
|
166
|
-
return (0, utils_1.
|
|
172
|
+
return (0, utils_1.initReducerArguments)(config, entityList, "");
|
|
167
173
|
}
|
|
168
174
|
/**
|
|
169
175
|
* Select entity type
|
|
@@ -172,7 +178,8 @@ function exploreReducer(state, action, exploreContext) {
|
|
|
172
178
|
if (payload === state.tabValue) {
|
|
173
179
|
return state;
|
|
174
180
|
}
|
|
175
|
-
|
|
181
|
+
const entityState = (0, utils_2.getEntityState)((0, utils_2.getEntityCategoryGroupConfigKey)(payload, state.entityPageState), state);
|
|
182
|
+
return Object.assign(Object.assign({}, state), { categoryGroups: entityState.categoryGroups, categoryViews: entityState.categoryViews, filterCount: (0, utils_2.getFilterCount)(entityState.filterState), filterState: entityState.filterState, listItems: [], loading: true, paginationState: (0, utils_2.resetPage)(state.paginationState), tabValue: payload });
|
|
176
183
|
}
|
|
177
184
|
/**
|
|
178
185
|
* Toggle entity view
|
|
@@ -185,26 +192,20 @@ function exploreReducer(state, action, exploreContext) {
|
|
|
185
192
|
**/
|
|
186
193
|
case ExploreActionKind.UpdateFilter: {
|
|
187
194
|
const filterState = (0, useCategoryFilter_1.buildNextFilterState)(state.filterState, payload.categoryKey, payload.selectedValue, payload.selected);
|
|
188
|
-
|
|
189
|
-
return Object.assign(Object.assign({}, state), {
|
|
190
|
-
filterState }) }), filterCount,
|
|
191
|
-
filterState, paginationState: (0, utils_1.resetPage)(state.paginationState) });
|
|
195
|
+
(0, utils_2.updateEntityStateByCategoryGroupConfigKey)(state, { filterState });
|
|
196
|
+
return Object.assign(Object.assign({}, state), { filterCount: (0, utils_2.getFilterCount)(filterState), filterState, paginationState: (0, utils_2.resetPage)(state.paginationState) });
|
|
192
197
|
}
|
|
193
198
|
/**
|
|
194
199
|
* Update sorting
|
|
195
200
|
**/
|
|
196
201
|
case ExploreActionKind.UpdateSorting: {
|
|
197
|
-
|
|
198
|
-
const currentPageState = state.entityPageState[currentEntity];
|
|
199
|
-
return Object.assign(Object.assign({}, state), { entityPageState: Object.assign(Object.assign({}, state.entityPageState), { [currentEntity]: Object.assign(Object.assign({}, currentPageState), { sorting: payload }) }), paginationState: (0, utils_1.resetPage)(state.paginationState) });
|
|
202
|
+
return Object.assign(Object.assign({}, state), { entityPageState: (0, utils_2.updateEntityPageState)(state.tabValue, state.entityPageState, { sorting: payload }), paginationState: (0, utils_2.resetPage)(state.paginationState) });
|
|
200
203
|
}
|
|
201
204
|
/**
|
|
202
205
|
* Update column visibility
|
|
203
206
|
**/
|
|
204
207
|
case ExploreActionKind.UpdateColumnVisibility: {
|
|
205
|
-
|
|
206
|
-
const currentPageState = state.entityPageState[currentEntity];
|
|
207
|
-
return Object.assign(Object.assign({}, state), { entityPageState: Object.assign(Object.assign({}, state.entityPageState), { [currentEntity]: Object.assign(Object.assign({}, currentPageState), { columnsVisibility: payload }) }) });
|
|
208
|
+
return Object.assign(Object.assign({}, state), { entityPageState: (0, utils_2.updateEntityPageState)(state.tabValue, state.entityPageState, { columnsVisibility: payload }) });
|
|
208
209
|
}
|
|
209
210
|
default:
|
|
210
211
|
return state;
|
|
@@ -68,14 +68,14 @@ const ExploreView = (props) => {
|
|
|
68
68
|
const { entities, explorerTitle, summaryConfig, trackingConfig } = config;
|
|
69
69
|
const { listView } = entityConfig;
|
|
70
70
|
const { listHero, subTitleHero } = listView || {};
|
|
71
|
-
const {
|
|
71
|
+
const { categoryGroups, categoryViews, filterCount, isRelatedView, tabValue, } = exploreState;
|
|
72
72
|
const { push } = (0, router_1.useRouter)();
|
|
73
73
|
const tabs = getTabs(entities);
|
|
74
74
|
const { response: summaryResponse } = (0, useSummary_1.useSummary)(); // Fetch summary.
|
|
75
75
|
(0, useEntityList_1.useEntityList)(props); // Fetch entities.
|
|
76
76
|
(0, useEntityListRelatedView_1.useEntityListRelatedView)(); // Fetch related entities.
|
|
77
77
|
const { entityListType } = props;
|
|
78
|
-
const categoryFilters = (0, react_1.useMemo)(() => buildCategoryFilters(categoryViews,
|
|
78
|
+
const categoryFilters = (0, react_1.useMemo)(() => buildCategoryFilters(categoryViews, categoryGroups), [categoryGroups, categoryViews]);
|
|
79
79
|
/**
|
|
80
80
|
* Closes filter drawer.
|
|
81
81
|
*/
|
|
@@ -159,14 +159,14 @@ exports.ExploreView = ExploreView;
|
|
|
159
159
|
/**
|
|
160
160
|
* Builds the category views into category views grouped by the given category group configuration.
|
|
161
161
|
* @param selectCategoryViews - View models of categories to display.
|
|
162
|
-
* @param
|
|
162
|
+
* @param categoryGroups - Category groups.
|
|
163
163
|
* @returns category filters.
|
|
164
164
|
*/
|
|
165
|
-
function buildCategoryFilters(selectCategoryViews,
|
|
166
|
-
if (!
|
|
165
|
+
function buildCategoryFilters(selectCategoryViews, categoryGroups) {
|
|
166
|
+
if (!categoryGroups) {
|
|
167
167
|
return [{ categoryViews: selectCategoryViews }];
|
|
168
168
|
}
|
|
169
|
-
return
|
|
169
|
+
return categoryGroups.map(({ categoryConfigs, label }) => {
|
|
170
170
|
// Grab the category views for the configured grouped categories.
|
|
171
171
|
const categoryViews = categoryConfigs.reduce((acc, { key: categoryKey }) => {
|
|
172
172
|
const categoryView = selectCategoryViews.find(({ key }) => key === categoryKey);
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@ import { Button } from "@mui/material";
|
|
|
3
3
|
import { mediaDesktopSmallUp } from "../../../../styles/common/mixins/breakpoints";
|
|
4
4
|
|
|
5
5
|
export const FilterLabel = styled(Button)`
|
|
6
|
-
|
|
6
|
+
font-weight: inherit;
|
|
7
7
|
gap: 0;
|
|
8
8
|
justify-content: space-between;
|
|
9
9
|
padding: 10px 16px;
|
|
@@ -15,7 +15,6 @@ export const FilterLabel = styled(Button)`
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
&.Mui-disabled {
|
|
18
|
-
color: ${({ theme }) => theme.palette.ink.main};
|
|
19
18
|
opacity: 0.3;
|
|
20
19
|
}
|
|
21
20
|
|
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
import { css } from "@emotion/react";
|
|
2
2
|
import styled from "@emotion/styled";
|
|
3
3
|
import { mediaDesktopSmallUp } from "../../../../styles/common/mixins/breakpoints";
|
|
4
|
+
import { inkLight, inkMain } from "../../../../styles/common/mixins/colors";
|
|
5
|
+
import {
|
|
6
|
+
textBody400,
|
|
7
|
+
textBody500,
|
|
8
|
+
} from "../../../../styles/common/mixins/fonts";
|
|
4
9
|
|
|
5
10
|
interface Props {
|
|
6
11
|
disabled: boolean;
|
|
7
12
|
height: number;
|
|
13
|
+
isBaseStyle: boolean;
|
|
8
14
|
}
|
|
9
15
|
|
|
10
16
|
export const Filters = styled("div", {
|
|
11
17
|
shouldForwardProp: (prop) => prop !== "height",
|
|
12
18
|
})<Props>`
|
|
19
|
+
${(props) => (props.isBaseStyle ? textBody500(props) : textBody400(props))};
|
|
20
|
+
color: ${(props) => (props.isBaseStyle ? inkMain(props) : inkLight(props))};
|
|
13
21
|
height: ${({ height }) => height}px;
|
|
14
22
|
margin: 8px 0;
|
|
15
23
|
overflow: auto;
|
|
@@ -32,3 +40,13 @@ export const Filters = styled("div", {
|
|
|
32
40
|
padding: 0 12px 0 16px;
|
|
33
41
|
}
|
|
34
42
|
`;
|
|
43
|
+
|
|
44
|
+
export const CategoryViewsLabel = styled("div")`
|
|
45
|
+
${textBody500};
|
|
46
|
+
color: ${inkMain};
|
|
47
|
+
padding: 8px 16px;
|
|
48
|
+
|
|
49
|
+
${mediaDesktopSmallUp} {
|
|
50
|
+
padding: 8px 0;
|
|
51
|
+
}
|
|
52
|
+
`;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Divider } from "@mui/material";
|
|
2
2
|
import { TrackFilterOpenedFunction } from "config/entities";
|
|
3
|
-
import React, { Fragment, useEffect, useRef, useState } from "react";
|
|
3
|
+
import React, { Fragment, useEffect, useMemo, useRef, useState } from "react";
|
|
4
4
|
import { CategoryTag, SelectCategoryView } from "../../../../common/entities";
|
|
5
5
|
import {
|
|
6
6
|
BREAKPOINT_FN_NAME,
|
|
@@ -11,7 +11,7 @@ import { useWindowResize } from "../../../../hooks/useWindowResize";
|
|
|
11
11
|
import { DESKTOP_SM } from "../../../../theme/common/breakpoints";
|
|
12
12
|
import { Filter } from "../Filter/filter";
|
|
13
13
|
import { FilterTags } from "../FilterTags/filterTags";
|
|
14
|
-
import { Filters as FilterList } from "./filters.styles";
|
|
14
|
+
import { CategoryViewsLabel, Filters as FilterList } from "./filters.styles";
|
|
15
15
|
|
|
16
16
|
export interface CategoryFilter {
|
|
17
17
|
categoryViews: SelectCategoryView[];
|
|
@@ -76,16 +76,26 @@ export const Filters = ({
|
|
|
76
76
|
const { height: windowHeight } = useWindowResize();
|
|
77
77
|
const filterListRef = useRef<HTMLDivElement>(null);
|
|
78
78
|
const [height, setHeight] = useState<number>(0);
|
|
79
|
+
const isLabeled = useMemo(
|
|
80
|
+
() => isCategoryViewsLabeled(categoryFilters),
|
|
81
|
+
[categoryFilters]
|
|
82
|
+
);
|
|
79
83
|
|
|
80
84
|
useEffect(() => {
|
|
81
85
|
setHeight(calculateListHeight(windowHeight, filterListRef.current));
|
|
82
86
|
}, [windowHeight]);
|
|
83
87
|
|
|
84
88
|
return (
|
|
85
|
-
<FilterList
|
|
89
|
+
<FilterList
|
|
90
|
+
disabled={disabled}
|
|
91
|
+
height={height}
|
|
92
|
+
isBaseStyle={!isLabeled}
|
|
93
|
+
ref={filterListRef}
|
|
94
|
+
>
|
|
86
95
|
{categoryFilters.map(({ categoryViews, label }, i) => (
|
|
87
96
|
<Fragment key={i}>
|
|
88
97
|
{i !== 0 && <Divider />}
|
|
98
|
+
{label && <CategoryViewsLabel>{label}</CategoryViewsLabel>}
|
|
89
99
|
{categoryViews.map((categoryView) => (
|
|
90
100
|
<Filter
|
|
91
101
|
key={categoryView.key}
|
|
@@ -116,3 +126,12 @@ function calculateListHeight(
|
|
|
116
126
|
): number {
|
|
117
127
|
return windowHeight - (filterListEl?.getBoundingClientRect()?.top ?? 0);
|
|
118
128
|
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Returns true if any category views are labelled.
|
|
132
|
+
* @param categoryFilters - Category filters.
|
|
133
|
+
* @returns true if any category views are labelled.
|
|
134
|
+
*/
|
|
135
|
+
function isCategoryViewsLabeled(categoryFilters: CategoryFilter[]): boolean {
|
|
136
|
+
return categoryFilters.some(({ label }) => label);
|
|
137
|
+
}
|
|
@@ -31,7 +31,7 @@ import { useExploreMode } from "../../hooks/useExploreMode";
|
|
|
31
31
|
import { useExploreState } from "../../hooks/useExploreState";
|
|
32
32
|
import { useScroll } from "../../hooks/useScroll";
|
|
33
33
|
import { ENTITY_VIEW, ExploreActionKind } from "../../providers/exploreState";
|
|
34
|
-
import { DEFAULT_PAGINATION_STATE } from "../../providers/exploreState/constants";
|
|
34
|
+
import { DEFAULT_PAGINATION_STATE } from "../../providers/exploreState/initializer/constants";
|
|
35
35
|
import { TABLET } from "../../theme/common/breakpoints";
|
|
36
36
|
import { FluidPaper, GridPaper } from "../common/Paper/paper.styles";
|
|
37
37
|
import { NoResults } from "../NoResults/noResults";
|
package/src/config/entities.ts
CHANGED
|
@@ -51,9 +51,17 @@ export interface BackPageTabConfig extends TabConfig {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
/**
|
|
54
|
-
* Model of
|
|
54
|
+
* Model of category group config in site config.
|
|
55
55
|
*/
|
|
56
56
|
export interface CategoryGroupConfig {
|
|
57
|
+
categoryGroups: CategoryGroup[];
|
|
58
|
+
key: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Model of grouped configured categories in site config.
|
|
63
|
+
*/
|
|
64
|
+
export interface CategoryGroup {
|
|
57
65
|
categoryConfigs: CategoryConfig[];
|
|
58
66
|
label?: string;
|
|
59
67
|
}
|
|
@@ -143,7 +151,7 @@ export type EntityPath = string;
|
|
|
143
151
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- This config model is part of a generic array
|
|
144
152
|
export interface EntityConfig<T = any, I = any> extends TabConfig {
|
|
145
153
|
apiPath?: EntityPath;
|
|
146
|
-
|
|
154
|
+
categoryGroupConfig?: CategoryGroupConfig;
|
|
147
155
|
detail: BackPageConfig;
|
|
148
156
|
entityMapper?: EntityMapper<T, I>;
|
|
149
157
|
exploreMode: ExploreMode;
|
|
@@ -358,7 +366,7 @@ export interface SiteConfig {
|
|
|
358
366
|
appTitle: string;
|
|
359
367
|
authentication?: AuthenticationConfig;
|
|
360
368
|
browserURL: string;
|
|
361
|
-
|
|
369
|
+
categoryGroupConfig?: CategoryGroupConfig;
|
|
362
370
|
contentDir?: string;
|
|
363
371
|
contentThemeOptionsFn?: ThemeOptionsFn;
|
|
364
372
|
dataSource: DataSourceConfig;
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
import { EntityMapper } from "../config/entities";
|
|
13
13
|
import { getEntityConfig } from "../config/utils";
|
|
14
14
|
import { ExploreActionKind } from "../providers/exploreState";
|
|
15
|
-
import { DEFAULT_PAGINATION_STATE } from "../providers/exploreState/constants";
|
|
15
|
+
import { DEFAULT_PAGINATION_STATE } from "../providers/exploreState/initializer/constants";
|
|
16
16
|
import { useAsync } from "./useAsync";
|
|
17
17
|
import { useAuthentication } from "./useAuthentication/useAuthentication";
|
|
18
18
|
import { useConfig } from "./useConfig";
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import Router from "next/router";
|
|
2
|
+
import { useCallback, useEffect, useState } from "react";
|
|
3
|
+
import { useConfig } from "./useConfig";
|
|
2
4
|
import { useLocation } from "./useLocation";
|
|
3
5
|
|
|
4
6
|
export const INACTIVITY_PARAM = "inactivityTimeout";
|
|
@@ -13,15 +15,19 @@ interface UseSessionTimeout {
|
|
|
13
15
|
* @returns flag indicating if the session has timed out.
|
|
14
16
|
*/
|
|
15
17
|
export const useSessionTimeout = (): UseSessionTimeout => {
|
|
18
|
+
const {
|
|
19
|
+
config: { redirectRootToPath },
|
|
20
|
+
} = useConfig();
|
|
16
21
|
const [isSessionTimeout, setIsSessionTimeout] = useState<boolean>(false);
|
|
17
22
|
// Get the session timeout from URL parameters.
|
|
18
23
|
const { search } = useLocation() || {};
|
|
19
24
|
const sessionTimeout = search?.get(INACTIVITY_PARAM);
|
|
20
25
|
|
|
21
26
|
// Clears session timeout state.
|
|
22
|
-
const clearSessionTimeout = (): void => {
|
|
27
|
+
const clearSessionTimeout = useCallback((): void => {
|
|
23
28
|
setIsSessionTimeout(false);
|
|
24
|
-
|
|
29
|
+
Router.replace(redirectRootToPath);
|
|
30
|
+
}, [redirectRootToPath]);
|
|
25
31
|
|
|
26
32
|
useEffect(() => {
|
|
27
33
|
setIsSessionTimeout(sessionTimeout === "true");
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ColumnSort } from "@tanstack/react-table";
|
|
2
|
+
import { SelectCategory, SelectedFilter } from "../../common/entities";
|
|
3
|
+
import {
|
|
4
|
+
CategoryConfig,
|
|
5
|
+
CategoryGroup,
|
|
6
|
+
CategoryGroupConfig,
|
|
7
|
+
EntityPath,
|
|
8
|
+
} from "../../config/entities";
|
|
9
|
+
|
|
10
|
+
export interface EntityPageState {
|
|
11
|
+
categoryGroupConfigKey: CategoryGroupConfigKey;
|
|
12
|
+
columnsVisibility: Record<string, boolean>;
|
|
13
|
+
sorting: ColumnSort[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface EntityPageStateMapper {
|
|
17
|
+
[key: EntityPath]: EntityPageState;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface EntityState {
|
|
21
|
+
categoryConfigs?: CategoryConfig[];
|
|
22
|
+
categoryGroups?: CategoryGroup[];
|
|
23
|
+
categoryViews: SelectCategory[];
|
|
24
|
+
filterState: SelectedFilter[];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type EntityStateByCategoryGroupConfigKey = Map<
|
|
28
|
+
CategoryGroupConfigKey,
|
|
29
|
+
EntityState
|
|
30
|
+
>;
|
|
31
|
+
|
|
32
|
+
export type CategoryGroupConfigKey = CategoryGroupConfig["key"];
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
import { ExploreState, PaginationState } from "
|
|
1
|
+
import { ExploreState, PaginationState } from "../../exploreState";
|
|
2
|
+
import { EntityState } from "../entities";
|
|
3
|
+
|
|
4
|
+
export const DEFAULT_ENTITY_STATE: EntityState = {
|
|
5
|
+
categoryViews: [],
|
|
6
|
+
filterState: [],
|
|
7
|
+
};
|
|
2
8
|
|
|
3
|
-
// Template constants
|
|
4
9
|
export const DEFAULT_PAGINATION_STATE: PaginationState = {
|
|
5
10
|
currentPage: 1,
|
|
6
11
|
index: null,
|
|
@@ -11,11 +16,11 @@ export const DEFAULT_PAGINATION_STATE: PaginationState = {
|
|
|
11
16
|
rows: 0,
|
|
12
17
|
};
|
|
13
18
|
|
|
14
|
-
// Initial state
|
|
15
19
|
export const INITIAL_STATE: ExploreState = {
|
|
16
20
|
catalogState: undefined,
|
|
17
21
|
categoryViews: [],
|
|
18
22
|
entityPageState: {},
|
|
23
|
+
entityStateByCategoryGroupConfigKey: new Map(),
|
|
19
24
|
featureFlagState: undefined,
|
|
20
25
|
filterCount: 0,
|
|
21
26
|
filterState: [],
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { SelectedFilter } from "../../../common/entities";
|
|
2
|
+
import { getInitialTableColumnVisibility } from "../../../components/Table/common/utils";
|
|
3
|
+
import {
|
|
4
|
+
CategoryConfig,
|
|
5
|
+
CategoryGroup,
|
|
6
|
+
CategoryGroupConfig,
|
|
7
|
+
EntityConfig,
|
|
8
|
+
SiteConfig,
|
|
9
|
+
} from "../../../config/entities";
|
|
10
|
+
import { getDefaultSorting } from "../../../config/utils";
|
|
11
|
+
import { ExploreState } from "../../exploreState";
|
|
12
|
+
import {
|
|
13
|
+
CategoryGroupConfigKey,
|
|
14
|
+
EntityPageStateMapper,
|
|
15
|
+
EntityStateByCategoryGroupConfigKey,
|
|
16
|
+
} from "../entities";
|
|
17
|
+
import { getEntityCategoryGroupConfigKey, getFilterCount } from "../utils";
|
|
18
|
+
import { DEFAULT_ENTITY_STATE, INITIAL_STATE } from "./constants";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Returns entity related configured category group config where entity config takes precedence over site config.
|
|
22
|
+
* @param siteConfig - Site config.
|
|
23
|
+
* @param entityConfig - Entity config.
|
|
24
|
+
* @returns entity related category group config.
|
|
25
|
+
*/
|
|
26
|
+
function getEntityCategoryGroupConfig(
|
|
27
|
+
siteConfig: SiteConfig,
|
|
28
|
+
entityConfig: EntityConfig
|
|
29
|
+
): CategoryGroupConfig | undefined {
|
|
30
|
+
const siteCategoryGroupConfig = siteConfig.categoryGroupConfig;
|
|
31
|
+
const entityCategoryGroupConfig = entityConfig.categoryGroupConfig;
|
|
32
|
+
return entityCategoryGroupConfig ?? siteCategoryGroupConfig;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Returns configured category groups as a list of configured categories.
|
|
37
|
+
* @param categoryGroups - Configured category groups.
|
|
38
|
+
* @returns a list of configured categories.
|
|
39
|
+
*/
|
|
40
|
+
function flattenCategoryGroups(
|
|
41
|
+
categoryGroups?: CategoryGroup[]
|
|
42
|
+
): CategoryConfig[] | undefined {
|
|
43
|
+
return categoryGroups?.flatMap(({ categoryConfigs }) => categoryConfigs);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Initializes category group config key for the entity.
|
|
48
|
+
* @param siteConfig - Site config.
|
|
49
|
+
* @param entityConfig - Entity config.
|
|
50
|
+
* @returns category group config key.
|
|
51
|
+
*/
|
|
52
|
+
function initCategoryGroupConfigKey(
|
|
53
|
+
siteConfig: SiteConfig,
|
|
54
|
+
entityConfig: EntityConfig
|
|
55
|
+
): CategoryGroupConfigKey {
|
|
56
|
+
const categoryGroupConfig = getEntityCategoryGroupConfig(
|
|
57
|
+
siteConfig,
|
|
58
|
+
entityConfig
|
|
59
|
+
);
|
|
60
|
+
return categoryGroupConfig?.key || entityConfig.route;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Initializes category groups for the current entity.
|
|
65
|
+
* @param entityStateByCategoryGroupConfigKey - Entity state by category group config key.
|
|
66
|
+
* @param categoryGroupConfigKey - Category group config key.
|
|
67
|
+
* @returns category groups.
|
|
68
|
+
*/
|
|
69
|
+
function initCategoryGroups(
|
|
70
|
+
entityStateByCategoryGroupConfigKey: EntityStateByCategoryGroupConfigKey,
|
|
71
|
+
categoryGroupConfigKey: CategoryGroupConfigKey
|
|
72
|
+
): CategoryGroup[] | undefined {
|
|
73
|
+
return entityStateByCategoryGroupConfigKey.get(categoryGroupConfigKey)
|
|
74
|
+
?.categoryGroups;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Initializes entity page state.
|
|
79
|
+
* @param config - Site config.
|
|
80
|
+
* @returns entity page state.
|
|
81
|
+
*/
|
|
82
|
+
function initEntityPageState(config: SiteConfig): EntityPageStateMapper {
|
|
83
|
+
return config.entities.reduce((acc, entity): EntityPageStateMapper => {
|
|
84
|
+
return {
|
|
85
|
+
...acc,
|
|
86
|
+
[entity.route]: {
|
|
87
|
+
categoryGroupConfigKey: initCategoryGroupConfigKey(config, entity),
|
|
88
|
+
columnsVisibility: getInitialTableColumnVisibility(entity.list.columns),
|
|
89
|
+
sorting: getDefaultSorting(entity),
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}, {} as EntityPageStateMapper);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Initializes entity state by category group config key.
|
|
97
|
+
* @param config - Site config.
|
|
98
|
+
* @param categoryGroupConfigKey - Category group config key.
|
|
99
|
+
* @param filterState - Filter state.
|
|
100
|
+
* @returns entity state by category group config key.
|
|
101
|
+
*/
|
|
102
|
+
function initEntityStateByCategoryGroupConfigKey(
|
|
103
|
+
config: SiteConfig,
|
|
104
|
+
categoryGroupConfigKey: CategoryGroupConfigKey,
|
|
105
|
+
filterState: SelectedFilter[]
|
|
106
|
+
): EntityStateByCategoryGroupConfigKey {
|
|
107
|
+
const entityStateByCategoryGroupConfigKey: EntityStateByCategoryGroupConfigKey =
|
|
108
|
+
new Map();
|
|
109
|
+
for (const entity of config.entities) {
|
|
110
|
+
const categoryGroupConfig = getEntityCategoryGroupConfig(config, entity);
|
|
111
|
+
if (!categoryGroupConfig) continue;
|
|
112
|
+
const { categoryGroups, key } = categoryGroupConfig;
|
|
113
|
+
if (entityStateByCategoryGroupConfigKey.has(key)) continue;
|
|
114
|
+
entityStateByCategoryGroupConfigKey.set(key, {
|
|
115
|
+
...DEFAULT_ENTITY_STATE,
|
|
116
|
+
categoryConfigs: flattenCategoryGroups(categoryGroups),
|
|
117
|
+
categoryGroups,
|
|
118
|
+
filterState: key === categoryGroupConfigKey ? filterState : [],
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
return entityStateByCategoryGroupConfigKey;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Initializes filter state from URL "filter" parameter.
|
|
126
|
+
* @param decodedFilterParam - Decoded filter parameter.
|
|
127
|
+
* @returns filter state.
|
|
128
|
+
*/
|
|
129
|
+
function initFilterState(decodedFilterParam: string): SelectedFilter[] {
|
|
130
|
+
// Define filter state, from URL "filter" parameter, if present and valid.
|
|
131
|
+
let filterState: SelectedFilter[] = [];
|
|
132
|
+
try {
|
|
133
|
+
filterState = JSON.parse(decodedFilterParam);
|
|
134
|
+
} catch {
|
|
135
|
+
// do nothing
|
|
136
|
+
}
|
|
137
|
+
return filterState;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Returns the explore state reducer initial arguments.
|
|
142
|
+
* @param config - Site config.
|
|
143
|
+
* @param entityListType - Entity list type.
|
|
144
|
+
* @param decodedFilterParam - Decoded filter parameter.
|
|
145
|
+
* @param decodedCatalogParam - Decoded catalog parameter.
|
|
146
|
+
* @param decodedFeatureFlagParam - Decoded feature flag parameter.
|
|
147
|
+
* @returns explore state reducer initial arguments.
|
|
148
|
+
*/
|
|
149
|
+
export function initReducerArguments(
|
|
150
|
+
config: SiteConfig,
|
|
151
|
+
entityListType: string,
|
|
152
|
+
decodedFilterParam: string,
|
|
153
|
+
decodedCatalogParam?: string,
|
|
154
|
+
decodedFeatureFlagParam?: string
|
|
155
|
+
): ExploreState {
|
|
156
|
+
const filterState = initFilterState(decodedFilterParam);
|
|
157
|
+
const entityPageState = initEntityPageState(config);
|
|
158
|
+
const categoryGroupConfigKey = getEntityCategoryGroupConfigKey(
|
|
159
|
+
entityListType,
|
|
160
|
+
entityPageState
|
|
161
|
+
);
|
|
162
|
+
const entityStateByCategoryGroupConfigKey =
|
|
163
|
+
initEntityStateByCategoryGroupConfigKey(
|
|
164
|
+
config,
|
|
165
|
+
categoryGroupConfigKey,
|
|
166
|
+
filterState
|
|
167
|
+
);
|
|
168
|
+
const categoryGroups = initCategoryGroups(
|
|
169
|
+
entityStateByCategoryGroupConfigKey,
|
|
170
|
+
categoryGroupConfigKey
|
|
171
|
+
);
|
|
172
|
+
return {
|
|
173
|
+
...INITIAL_STATE,
|
|
174
|
+
catalogState: decodedCatalogParam,
|
|
175
|
+
categoryGroups,
|
|
176
|
+
entityPageState,
|
|
177
|
+
entityStateByCategoryGroupConfigKey,
|
|
178
|
+
featureFlagState: decodedFeatureFlagParam,
|
|
179
|
+
filterCount: getFilterCount(filterState),
|
|
180
|
+
filterState,
|
|
181
|
+
tabValue: entityListType,
|
|
182
|
+
};
|
|
183
|
+
}
|