Commit f814dc88 authored by Viacheslav Pavlov's avatar Viacheslav Pavlov
Browse files

Added ui part for Datasets overview

Fixed displaying of partner overview

fixed SSR

Fixed Map deserializing after SSR

Changed order of Overview and Browse pages
parent c469a6b5
...@@ -959,6 +959,11 @@ ...@@ -959,6 +959,11 @@
"DIGITIZER": "Digitizes data.", "DIGITIZER": "Digitizes data.",
"CURATOR": "Organizes and validates data and metadata in correct format, ensures quality of both." "CURATOR": "Organizes and validates data and metadata in correct format, ensures quality of both."
} }
},
"overview": {
"owner": "Data provider",
"crops": "Crop",
"rights": "Licence"
} }
}, },
"dashboard": { "dashboard": {
...@@ -1171,6 +1176,10 @@ ...@@ -1171,6 +1176,10 @@
"descriptorCountDesc": "Descriptor count (high to low)", "descriptorCountDesc": "Descriptor count (high to low)",
"startDate": "Experiment start date", "startDate": "Experiment start date",
"endDate": "Experiment end date" "endDate": "Experiment end date"
},
"tab": {
"data": "Datasets",
"overview": "Overview"
} }
}, },
"descriptorlists": { "descriptorlists": {
......
...@@ -4,13 +4,14 @@ import navigateTo from 'actions/navigation'; ...@@ -4,13 +4,14 @@ import navigateTo from 'actions/navigation';
import { showSnackbar } from 'actions/snackbar'; import { showSnackbar } from 'actions/snackbar';
import {createApiCaller, createPureApiCaller} from 'actions/ApiCall'; import {createApiCaller, createPureApiCaller} from 'actions/ApiCall';
// Constants // Constants
import {RECEIVE_DATASET, APPEND_DATASET_PAGE, APPEND_ACCESSIONS_PAGE} from 'datasets/constants'; import {RECEIVE_DATASET, APPEND_DATASET_PAGE, APPEND_ACCESSIONS_PAGE, RECEIVE_DATASET_OVERVIEW} from 'datasets/constants';
// Model // Model
import Dataset from 'model/catalog/Dataset'; import Dataset from 'model/catalog/Dataset';
import DatasetFilter from 'model/catalog/DatasetFilter'; import DatasetFilter from 'model/catalog/DatasetFilter';
import Page from 'model/Page'; import Page from 'model/Page';
import FilteredPage, { IPageRequest } from 'model/FilteredPage'; import FilteredPage, { IPageRequest } from 'model/FilteredPage';
import { AccessionRef } from 'model/accession/AccessionRef'; import { AccessionRef } from 'model/accession/AccessionRef';
import DatasetOverview from 'model/catalog/DatasetOverview';
// Service // Service
import DatasetService from 'service/catalog/DatasetService'; import DatasetService from 'service/catalog/DatasetService';
// Util // Util
...@@ -22,6 +23,7 @@ const apiLoadDataset = createApiCaller(DatasetService.getDataset, RECEIVE_DATASE ...@@ -22,6 +23,7 @@ const apiLoadDataset = createApiCaller(DatasetService.getDataset, RECEIVE_DATASE
const apiLoadDatasetAccesions = createApiCaller(DatasetService.listAccessions, APPEND_ACCESSIONS_PAGE); const apiLoadDatasetAccesions = createApiCaller(DatasetService.listAccessions, APPEND_ACCESSIONS_PAGE);
const apiListDatasets = createApiCaller(DatasetService.datasetList, APPEND_DATASET_PAGE); const apiListDatasets = createApiCaller(DatasetService.datasetList, APPEND_DATASET_PAGE);
const apiOverviewDatasets = createApiCaller(DatasetService.overview, RECEIVE_DATASET_OVERVIEW);
const apiListDatasetsPure = createPureApiCaller(DatasetService.datasetList); const apiListDatasetsPure = createPureApiCaller(DatasetService.datasetList);
...@@ -68,4 +70,20 @@ const promiselistDatasets = (page?, results?, sortBy?: string[], filter?, order? ...@@ -68,4 +70,20 @@ const promiselistDatasets = (page?, results?, sortBy?: string[], filter?, order?
}); });
}; };
export const updateOverviewRoute = (overview: DatasetOverview) => (dispatch) => {
dispatch(navigateTo(overview && overview.filterCode ? `/datasets/overview/${overview.filterCode}` : '/datasets/overview'));
};
export const applyOverviewFilters = (filters: string | DatasetFilter) => (dispatch) => {
dispatch(showSnackbar('Applying filters...'));
return dispatch(apiOverviewDatasets(filters))
.then((overview) => {
dispatch(updateOverviewRoute(overview));
dispatch(showSnackbar(`Filters applied.`));
});
};
export { loadMoreDatasets, promiselistDatasets, loadDataset, loadMoreAccessions }; export { loadMoreDatasets, promiselistDatasets, loadDataset, loadMoreAccessions };
...@@ -4,6 +4,7 @@ export const DATASET_LIST_OF_ACCESSION_FORM = 'Form/DATASET_LIST_OF_ACCESSION_FO ...@@ -4,6 +4,7 @@ export const DATASET_LIST_OF_ACCESSION_FORM = 'Form/DATASET_LIST_OF_ACCESSION_FO
export const CREATE_DATASET = 'App/Dataset/RECEIVE_DATASET'; export const CREATE_DATASET = 'App/Dataset/RECEIVE_DATASET';
export const RECEIVE_DATASET = 'App/RECEIVE_DATASET'; export const RECEIVE_DATASET = 'App/RECEIVE_DATASET';
export const APPEND_DATASET_PAGE = 'App/APPEND_DATASET_PAGE'; export const APPEND_DATASET_PAGE = 'App/APPEND_DATASET_PAGE';
export const RECEIVE_DATASET_OVERVIEW = 'App/RECEIVE_DATASET_OVERVIEW';
export const APPEND_ACCESSIONS_PAGE = 'App/APPEND_ACCESSIONS_PAGE'; export const APPEND_ACCESSIONS_PAGE = 'App/APPEND_ACCESSIONS_PAGE';
// dashboard // dashboard
......
import update from 'immutability-helper'; import update from 'immutability-helper';
import * as _ from 'lodash'; import * as _ from 'lodash';
import {APPEND_DATASET_PAGE, RECEIVE_DATASET, APPEND_ACCESSIONS_PAGE} from 'datasets/constants'; import {
APPEND_DATASET_PAGE,
RECEIVE_DATASET,
APPEND_ACCESSIONS_PAGE,
RECEIVE_DATASET_OVERVIEW,
} from 'datasets/constants';
import { LOGIN_APP, LOGIN_USER, LOGOUT } from 'constants/login'; import { LOGIN_APP, LOGIN_USER, LOGOUT } from 'constants/login';
...@@ -10,15 +15,18 @@ import { AccessionRef } from 'model/accession/AccessionRef'; ...@@ -10,15 +15,18 @@ import { AccessionRef } from 'model/accession/AccessionRef';
import Page from 'model/Page'; import Page from 'model/Page';
import FilteredPage from 'model/FilteredPage'; import FilteredPage from 'model/FilteredPage';
import ApiCall from 'model/ApiCall'; import ApiCall from 'model/ApiCall';
import DatasetOverview from 'model/catalog/DatasetOverview';
import {dereferenceReferences} from 'utilities'; import {dereferenceReferences} from 'utilities';
const INITIAL_STATE: { const INITIAL_STATE: {
dataset: ApiCall<Dataset>, dataset: ApiCall<Dataset>,
paged: ApiCall<FilteredPage<Dataset>>, paged: ApiCall<FilteredPage<Dataset>>,
overview: ApiCall<DatasetOverview>,
accessionRefs: ApiCall<Page<AccessionRef>>, accessionRefs: ApiCall<Page<AccessionRef>>,
} = { } = {
dataset: null, dataset: null,
paged: null, paged: null,
overview: null,
accessionRefs: null, accessionRefs: null,
}; };
...@@ -87,6 +95,20 @@ function datasetsPublic(state = INITIAL_STATE, action: { type?: string, payload? ...@@ -87,6 +95,20 @@ function datasetsPublic(state = INITIAL_STATE, action: { type?: string, payload?
}); });
} }
case RECEIVE_DATASET_OVERVIEW: {
const { apiCall: { loading, error, timestamp, data } } = action.payload;
return update(state, {
overview: {
$set: {
loading,
error,
timestamp,
data: data !== undefined ? data : state.overview && state.overview.data,
},
},
});
}
default: default:
return state; return state;
} }
......
...@@ -16,6 +16,13 @@ const publicRoutes = [ ...@@ -16,6 +16,13 @@ const publicRoutes = [
subtitle: 'datasets.common.subtitle', subtitle: 'datasets.common.subtitle',
}, },
}, },
{
path: '/datasets/overview/:filterCode(v.+)?',
component: Loadable({
loader: () => import(/* webpackMode:"lazy", webpackChunkName: "datasets" */'datasets/ui/OverviewPage'),
}),
exact: true,
},
{ {
path: '/datasets/:filterCode(v.+)?', path: '/datasets/:filterCode(v.+)?',
component: Loadable({ component: Loadable({
......
...@@ -20,6 +20,11 @@ ...@@ -20,6 +20,11 @@
"DIGITIZER": "Digitizes data.", "DIGITIZER": "Digitizes data.",
"CURATOR": "Organizes and validates data and metadata in correct format, ensures quality of both." "CURATOR": "Organizes and validates data and metadata in correct format, ensures quality of both."
} }
},
"overview": {
"owner": "Data provider",
"crops": "Crop",
"rights": "Licence"
} }
}, },
"dashboard": { "dashboard": {
...@@ -232,5 +237,9 @@ ...@@ -232,5 +237,9 @@
"descriptorCountDesc": "Descriptor count (high to low)", "descriptorCountDesc": "Descriptor count (high to low)",
"startDate": "Experiment start date", "startDate": "Experiment start date",
"endDate": "Experiment end date" "endDate": "Experiment end date"
},
"tab": {
"data": "Datasets",
"overview": "Overview"
} }
} }
...@@ -19,6 +19,7 @@ import DatasetCard from './c/Card'; ...@@ -19,6 +19,7 @@ import DatasetCard from './c/Card';
import ContentHeader from 'ui/common/heading/ContentHeader'; import ContentHeader from 'ui/common/heading/ContentHeader';
import { ScrollToTopOnMount } from 'ui/common/page/scrollers'; import { ScrollToTopOnMount } from 'ui/common/page/scrollers';
import Tabs, { Tab } from 'ui/common/Tabs';
import BrowsePageTemplate from 'ui/pages/_base/BrowsePage'; import BrowsePageTemplate from 'ui/pages/_base/BrowsePage';
import { translate } from 'react-i18next'; import { translate } from 'react-i18next';
...@@ -27,7 +28,7 @@ class BrowsePage extends BrowsePageTemplate<Dataset> { ...@@ -27,7 +28,7 @@ class BrowsePage extends BrowsePageTemplate<Dataset> {
protected renderDataset = (d: Dataset) => <DatasetCard dataset={ d } key={ d.uuid }/>; protected renderDataset = (d: Dataset) => <DatasetCard dataset={ d } key={ d.uuid }/>;
public render() { public render() {
const { paged, t, loadMoreData, error, loading } = this.props; const { paged, t, loadMoreData, error, loading, currentTab, filterCode } = this.props;
return ( return (
<PageLayout sidebar={ <PageLayout sidebar={
...@@ -36,6 +37,10 @@ class BrowsePage extends BrowsePageTemplate<Dataset> { ...@@ -36,6 +37,10 @@ class BrowsePage extends BrowsePageTemplate<Dataset> {
<ScrollToTopOnMount/> <ScrollToTopOnMount/>
<PageTitle title={ t('datasets.common.modelName_plural') }/> <PageTitle title={ t('datasets.common.modelName_plural') }/>
<ContentHeader title={ t('datasets.common.modelName_plural') } subtitle={ t('datasets.public.p.browse.subtitle') }/> <ContentHeader title={ t('datasets.common.modelName_plural') } subtitle={ t('datasets.public.p.browse.subtitle') }/>
<Tabs tab={ currentTab }>
<Tab name="overview" to={ `/datasets/overview/${ filterCode || '' }` }>{ t('datasets.tab.overview') }</Tab>
<Tab name="data" to={ `/datasets/${ filterCode || '' }` }>{ t('datasets.tab.data') }</Tab>
</Tabs>
<PaginationComponent <PaginationComponent
pageObj={ paged } pageObj={ paged }
onSortChange={ this.onSortChange } onSortChange={ this.onSortChange }
...@@ -68,6 +73,7 @@ const mapStateToProps = (state, ownProps) => ({ ...@@ -68,6 +73,7 @@ const mapStateToProps = (state, ownProps) => ({
loading: state.datasets.public.paged ? state.datasets.public.paged.loading : false, loading: state.datasets.public.paged ? state.datasets.public.paged.loading : false,
error: state.datasets.public.paged ? state.datasets.public.paged.error : undefined, error: state.datasets.public.paged ? state.datasets.public.paged.error : undefined,
filterCode: ownProps.match.params.filterCode, filterCode: ownProps.match.params.filterCode,
currentTab: ownProps.match.params.tab || 'data', // current tab, or ownProps.location.pathname
}); });
const mapDispatchToProps = (dispatch) => bindActionCreators({ const mapDispatchToProps = (dispatch) => bindActionCreators({
......
import * as React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { translate } from 'react-i18next';
// actions
import { applyOverviewFilters, updateOverviewRoute } from 'datasets/actions/public';
import { showSnackbar } from 'actions/snackbar';
import { loadPartners } from 'partners/actions/public';
// model
import ApiCall from 'model/ApiCall';
import DatasetOverview from 'model/catalog/DatasetOverview';
import DatasetFilter from 'model/catalog/DatasetFilter';
// UI
import PageLayout, { PageContents } from 'ui/layout/PageLayout';
import Tabs, { Tab } from 'ui/common/Tabs';
import ContentHeader from 'ui/common/heading/ContentHeader';
import Number from 'ui/common/Number';
import Loading from 'ui/common/Loading';
import PageTitle from 'ui/common/PageTitle';
import PropertiesCard from 'ui/common/PropertiesCard';
import { PartnerLink } from 'ui/catalog/Links';
import GridContainer from 'ui/layout/GridContainer';
import PrettyFilters from 'ui/common/filter/PrettyFilters';
import DatasetFilters from 'datasets/ui/c/Filters';
interface IOverviewPageProps extends React.ClassAttributes<any> {
overview: ApiCall<DatasetOverview>;
applyOverviewFilters: (filter: string | DatasetFilter) => void;
updateOverviewRoute: (overview: DatasetOverview) => void;
showSnackbar: (message: string) => void;
partnerNames: any;
filterCode: string;
currentTab: string;
t: any;
}
class OverviewPage extends React.Component<IOverviewPageProps> {
protected static needs = [
({search, params: {filterCode}}) => {
return applyOverviewFilters(filterCode || '');
},
() => loadPartners({page: 0, size: 50}),
];
public componentWillMount() {
const {overview: apiCall, filterCode, applyOverviewFilters} = this.props;
const {data: overview} = apiCall || {data: undefined};
if (!overview || filterCode !== overview.filterCode) {
console.log('Applying filters', filterCode);
applyOverviewFilters(filterCode || '');
}
}
private addTerm = (property, term) => {
const {overview: apiCall, applyOverviewFilters, showSnackbar} = this.props;
const {data: overview} = apiCall;
const updatedFilter: DatasetFilter = {...overview.filter};
switch (property) {
case 'crops':
case 'owner.uuid':
case 'rights':
_.set(updatedFilter, property, _.concat(_.get(updatedFilter, property), term).filter((x) => x != null));
break;
// set
default:
_.set(updatedFilter, property, term);
}
console.log(`Updated filter for ${ property } +${ term }`, updatedFilter);
showSnackbar('Applying filters...');
applyOverviewFilters(updatedFilter);
}
public render() {
const {filterCode, currentTab, overview: apiCall, applyOverviewFilters, t} = this.props;
const partnerNames = new Map(this.props.partnerNames);
const {data: overviewWrapper, loading} = apiCall || {data: undefined, loading: true};
const overview = overviewWrapper && overviewWrapper.overview;
const overviewsTerms = new Map();
if (overview) {
Object.keys(overview).map((key) => {
const overviewEl = overview[key];
const terms = [].concat(overviewEl.terms,
{
term: 'Other',
count: overviewEl.other,
},
{
term: 'Not specified',
count: overviewEl.missing,
},
);
overviewsTerms.set(key, terms);
});
}
const filterByTerm = (property, term, count) => {
const skipTerms = ['Other', 'Missing', 'Not specified'];
return (
skipTerms.indexOf(term.term) === -1
? (<a onClick={ () => this.addTerm(property, term.term) }><Number value={ count }/></a>)
: (count)
);
};
return (
<PageLayout
sidebar={
<DatasetFilters initialValues={ overviewWrapper && overviewWrapper.filter || {} } onSubmit={ applyOverviewFilters }/>
}
withFooter
>
<PageTitle title={ t('datasets.common.modelName_plural') }/>
<ContentHeader
title={ t('datasets.common.modelName_plural') }
subtitle={ t('datasets.public.p.browse.subtitle') }
/>
<Tabs tab={ currentTab }>
<Tab name="overview" to={ `/datasets/overview/${ filterCode || '' }` }>{ t('datasets.tab.overview') }</Tab>
<Tab name="data" to={ `/datasets/${ filterCode || '' }` }>{ t('datasets.tab.data') }</Tab>
</Tabs>
<PrettyFilters
prefix="datasets"
filterObj={ overviewWrapper && overviewWrapper.filter || {} }
onSubmit={ applyOverviewFilters }
/>
{ loading && <Loading/> }
{ overview &&
<div>
<PageContents className="pt-1rem">
<GridContainer>
{ overviewsTerms && overviewsTerms.get('owner.uuid') && overviewsTerms.get('owner.uuid').length > 2 &&
<PropertiesCard
propertiesList={ overviewsTerms.get('owner.uuid').map((term) => (
{
title: partnerNames && partnerNames.size > 0 && partnerNames.get(term.term) ? <PartnerLink uuid={ term.term }>{ partnerNames.get(term.term) }</PartnerLink> : term.term,
value: filterByTerm('owner.uuid', term, term.count),
}
)) }
title={ t(`datasets.common.overview.owner`) }
propertyItemProps={ {numeric: true} }
small
/>
}
{ overviewsTerms && overviewsTerms.get('crops') && overviewsTerms.get('crops').length > 2 &&
<PropertiesCard
propertiesList={ overviewsTerms.get('crops').map((term) => ({ title: term.term, value: filterByTerm('crops', term, term.count) })) }
title={ t(`datasets.common.overview.crops`) }
propertyItemProps={ {numeric: true} }
small
/>
}
{ overviewsTerms && overviewsTerms.get('rights') && overviewsTerms.get('rights').length > 2 &&
<PropertiesCard
propertiesList={ overviewsTerms.get('rights').map((term) => ({ title: term.term, value: filterByTerm('rights', term, term.count) })) }
title={ t(`datasets.common.overview.rights`) }
propertyItemProps={ {numeric: true} }
small
/>
}
</GridContainer>
</PageContents>
</div>
}
</PageLayout>
);
}
}
const mapStateToProps = (state, ownProps) => ({
overview: state.datasets.public.overview,
partnerNames: state.uuidDecoder.labels,
filterCode: ownProps.match.params.filterCode || '',
currentTab: ownProps.match.params.tab || 'overview', // current tab, or ownProps.location.pathname
});
const mapDispatchToProps = (dispatch) => bindActionCreators({
applyOverviewFilters,
updateOverviewRoute,
showSnackbar,
}, dispatch);
export default translate()(connect(mapStateToProps, mapDispatchToProps)(OverviewPage));
...@@ -45,6 +45,7 @@ const DatasetFilters = ({ handleSubmit, initialize, t, ...other }) => ( ...@@ -45,6 +45,7 @@ const DatasetFilters = ({ handleSubmit, initialize, t, ...other }) => (
); );
export default translate()(reduxForm({ export default translate()(reduxForm({
destroyOnUnmount: false,
enableReinitialize: true, enableReinitialize: true,
form: DATASET_FILTERFORM, form: DATASET_FILTERFORM,
})(DatasetFilters)); })(DatasetFilters));
import DatasetFilter from 'model/catalog/DatasetFilter';
/*
* Defined in Swagger as '#/definitions/DatasetOverview'
*/
class DatasetOverview {
public datasetCount: number;
public filter: DatasetFilter;
public filterCode: string;
public overview: any;
}
export default DatasetOverview;
...@@ -10,6 +10,7 @@ import DatasetLocation from 'model/catalog/DatasetLocation'; ...@@ -10,6 +10,7 @@ import DatasetLocation from 'model/catalog/DatasetLocation';
import FilteredPage from 'model/FilteredPage'; import FilteredPage from 'model/FilteredPage';
import Page, { IPageRequest } from 'model/Page'; import Page, { IPageRequest } from 'model/Page';
import RepositoryFile from 'model/repository/RepositoryFile'; import RepositoryFile from 'model/repository/RepositoryFile';
import DatasetOverview from 'model/catalog/DatasetOverview';
const URL_LIST_ACCESSIONS = UrlTemplate.parse(`/api/v1/dataset/accessions/{uuid}`); const URL_LIST_ACCESSIONS = UrlTemplate.parse(`/api/v1/dataset/accessions/{uuid}`);
const URL_ADD_DESCRIPTORS = UrlTemplate.parse(`/api/v1/dataset/add-descriptors/{uuid},{version}`); const URL_ADD_DESCRIPTORS = UrlTemplate.parse(`/api/v1/dataset/add-descriptors/{uuid},{version}`);
...@@ -18,6 +19,7 @@ const URL_CREATE_DATASET = `/api/v1/dataset/create`; ...@@ -18,6 +19,7 @@ const URL_CREATE_DATASET = `/api/v1/dataset/create`;
const URL_REVIEW_DATASET = `/api/v1/dataset/for-review`; const URL_REVIEW_DATASET = `/api/v1/dataset/for-review`;
const URL_DATASET_LIST = `/api/v1/dataset/list`; const URL_DATASET_LIST = `/api/v1/dataset/list`;
const URL_MY_DATASETS = `/api/v1/dataset/list-mine`; const URL_MY_DATASETS = `/api/v1/dataset/list-mine`;
const URL_OVERVIEW = `/api/v1/dataset/overview`;
const URL_REMATCH_DATASET_ACCESSIONS = UrlTemplate.parse(`/api/v1/dataset/rematch-accessions/{uuid},{version}`); const URL_REMATCH_DATASET_ACCESSIONS = UrlTemplate.parse(`/api/v1/dataset/rematch-accessions/{uuid},{version}`);
const URL_REJECT_DATASET = `/api/v1/dataset/reject`; const URL_REJECT_DATASET = `/api/v1/dataset/reject`;
const URL_REMOVE_DESCRIPTORS = UrlTemplate.parse(`/api/v1/dataset/remove-descriptors/{uuid},{version}`); const URL_REMOVE_DESCRIPTORS = UrlTemplate.parse(`/api/v1/dataset/remove-descriptors/{uuid},{version}`);
...@@ -238,6 +240,29 @@ class DatasetService { ...@@ -238,6 +240,29 @@ class DatasetService {
}).then(({ data }) => data as FilteredPage<Dataset>); }).then(({ data }) => data as FilteredPage<Dataset>);
} }
/**
* overview at /api/v1/dataset/overview
*
* @param filter filter
* @param xhrConfig additional xhr config
*/
public static overview(filter?: DatasetFilter, xhrConfig?: any): Promise<DatasetOverview> {
const qs = QueryString.stringify({
f: typeof filter === 'string' ? filter : undefined,
}, {});
const apiUrl = URL_OVERVIEW + (qs ? `?${qs}` : '');
// console.log(`Fetching from ${apiUrl}`);
const content = { data: typeof filter === 'string' ? null : { ...filter } };
return axiosBackend.request({
...xhrConfig,
url: apiUrl,
method: 'POST',
...content,
}).then(({ data }) => data as DatasetOverview);
}
/** /**
* rejectDataset at /api/v1/dataset/reject * rejectDataset at /api/v1/dataset/reject
* *
......
...@@ -174,7 +174,7 @@ function getLabelName(path, value, lookups, prefix, t, labels) { ...@@ -174,7 +174,7 @@ function getLabelName(path, value, lookups, prefix, t, labels) {
} }
const prettyPath: string = path const prettyPath: string = path
// remove array indexes square brackets [0], [1]... from path. // remove array indexes square brackets [0], [1]... from path.
.replace(/\[(.+?)\]/g, '') .replace(/\[(.+?)\]/g, '')
// split path title.eq -> ['title', 'eq'] // split path title.eq -> ['title', 'eq']
.split('.') .split('.')
......
...@@ -55,7 +55,7 @@ const PUBLIC_MENUS = [ ...@@ -55,7 +55,7 @@ const PUBLIC_MENUS = [
label: 'public.menu.exploreSubsets', label: 'public.menu.exploreSubsets',
}, },
{ {
to: '/datasets', to: '/datasets/overview',
label: 'public.menu.exploreDatasets', label: 'public.menu.exploreDatasets',