diff --git a/assets/less/zenodo-rdm/globals/site.overrides b/assets/less/zenodo-rdm/globals/site.overrides
index 8fb03858..c88fdd0c 100644
--- a/assets/less/zenodo-rdm/globals/site.overrides
+++ b/assets/less/zenodo-rdm/globals/site.overrides
@@ -138,4 +138,9 @@ a.inverted {
.ui.medium.header {
font-size: 1.45rem;
}
+}
+
+.image.thumbnail-image {
+ height: 150px;
+ object-fit: cover;
}
\ No newline at end of file
diff --git a/site/zenodo_rdm/assets/semantic-ui/js/zenodo_rdm/src/blr-related-works/BlrSearch.js b/site/zenodo_rdm/assets/semantic-ui/js/zenodo_rdm/src/blr-related-works/BlrSearch.js
new file mode 100644
index 00000000..670c244d
--- /dev/null
+++ b/site/zenodo_rdm/assets/semantic-ui/js/zenodo_rdm/src/blr-related-works/BlrSearch.js
@@ -0,0 +1,125 @@
+// This file is part of Zenodo.
+// Copyright (C) 2023 CERN.
+//
+// Zenodo is free software; you can redistribute it
+// and/or modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// Zenodo is distributed in the hope that it will be
+// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Zenodo; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+// MA 02111-1307, USA.
+//
+// In applying this license, CERN does not
+// waive the privileges and immunities granted to it by virtue of its status
+// as an Intergovernmental Organization or submit itself to any jurisdiction.
+
+import React from 'react';
+import {
+ ReactSearchKit,
+ InvenioSearchApi,
+ SearchBar,
+ ResultsLoader,
+ ResultsMultiLayout,
+ Error,
+ EmptyResults,
+ Pagination,
+ BucketAggregation,
+ LayoutSwitcher
+} from 'react-searchkit';
+import { OverridableContext } from "react-overridable";
+import { apiConfig } from "./api/config";
+import { Grid, Segment, Container, Header } from 'semantic-ui-react';
+import { ResultsGridLayout, ResultsListLayout } from "./components/ResultsLayout";
+import { RecordGridItem, RecordListItem } from "./components/RecordItem";
+import { FilterContainer, Filter, FilterValues } from "./components/Filter";
+import { BlrSearchBar } from './components/BlrSearchBar';
+import { LayoutSwitchButtons } from './components/LayoutSwitchButtons';
+import { NoResults } from "./components/NoResults";
+
+const blrSearchAppID = "blrSearch";
+
+const overriddenComponents = {
+ [`${blrSearchAppID}.SearchBar.element`]: BlrSearchBar,
+ [`${blrSearchAppID}.ResultsGrid.container`]: ResultsGridLayout,
+ [`${blrSearchAppID}.ResultsGrid.item`]: RecordGridItem,
+ [`${blrSearchAppID}.ResultsList.container`]: ResultsListLayout,
+ [`${blrSearchAppID}.ResultsList.item`]: RecordListItem,
+ [`${blrSearchAppID}.BucketAggregation.element`]: FilterContainer,
+ [`${blrSearchAppID}.BucketAggregationContainer.element`]: Filter,
+ [`${blrSearchAppID}.BucketAggregationValues.element`]: FilterValues,
+ [`${blrSearchAppID}.LayoutSwitcher.element`]: LayoutSwitchButtons,
+ [`${blrSearchAppID}.EmptyResults.element`]: NoResults,
+}
+
+export const BlrSearch = ({ endpoint, recordDOI, resourceType }) => {
+
+ const relationType = (resourceType) => (
+ (resourceType === "Journal article" || resourceType === "Book chapter") ? "ispartof" : "haspart"
+ );
+
+ const queryString = (relationType, identifier) => (
+ `metadata.related_identifiers.relation_type.id:${relationType} AND metadata.related_identifiers.identifier:"${identifier}"`
+ );
+
+ const searchApi = new InvenioSearchApi(
+ //apiConfig(`${endpoint}?q=${queryString(relationType(resourceType), recordDOI)}`)
+ apiConfig(endpoint)
+ );
+
+ const initialState = {
+ queryString: queryString(relationType(resourceType), recordDOI),
+ sortBy: "bestmatch",
+ sortOrder: "asc",
+ page: 1,
+ size: 4,
+ layout: "list",
+ };
+
+ return (
+ <>
+ Related content
+
+
+ <>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+
+
+ >
+ );
+}
\ No newline at end of file
diff --git a/site/zenodo_rdm/assets/semantic-ui/js/zenodo_rdm/src/blr-related-works/api/config.js b/site/zenodo_rdm/assets/semantic-ui/js/zenodo_rdm/src/blr-related-works/api/config.js
new file mode 100644
index 00000000..0b9da7dc
--- /dev/null
+++ b/site/zenodo_rdm/assets/semantic-ui/js/zenodo_rdm/src/blr-related-works/api/config.js
@@ -0,0 +1,31 @@
+// This file is part of Zenodo.
+// Copyright (C) 2023 CERN.
+//
+// Zenodo is free software; you can redistribute it
+// and/or modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// Zenodo is distributed in the hope that it will be
+// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Zenodo; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+// MA 02111-1307, USA.
+//
+// In applying this license, CERN does not
+// waive the privileges and immunities granted to it by virtue of its status
+// as an Intergovernmental Organization or submit itself to any jurisdiction.
+
+export const apiConfig = (endpoint) => ({
+ axios: {
+ url: endpoint,
+ timeout: 5000,
+ headers: {
+ Accept: "application/vnd.inveniordm.v1+json",
+ },
+ },
+ });
\ No newline at end of file
diff --git a/site/zenodo_rdm/assets/semantic-ui/js/zenodo_rdm/src/blr-related-works/components/BlrSearchBar.js b/site/zenodo_rdm/assets/semantic-ui/js/zenodo_rdm/src/blr-related-works/components/BlrSearchBar.js
new file mode 100644
index 00000000..8aa097a7
--- /dev/null
+++ b/site/zenodo_rdm/assets/semantic-ui/js/zenodo_rdm/src/blr-related-works/components/BlrSearchBar.js
@@ -0,0 +1,78 @@
+// This file is part of Zenodo.
+// Copyright (C) 2023 CERN.
+//
+// Zenodo is free software; you can redistribute it
+// and/or modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// Zenodo is distributed in the hope that it will be
+// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Zenodo; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+// MA 02111-1307, USA.
+//
+// In applying this license, CERN does not
+// waive the privileges and immunities granted to it by virtue of its status
+// as an Intergovernmental Organization or submit itself to any jurisdiction.
+
+import React from "react";
+import { Input, Icon } from "semantic-ui-react";
+
+export const BlrSearchBar = ({
+ queryString,
+ onBtnSearchClick,
+ onInputChange,
+ onKeyPress,
+ placeholder,
+ actionProps,
+ uiProps,
+}) => {
+
+ return (
+
+ ),
+ onClick: () => {
+ onBtnSearchClick()
+ },
+ className: "p-10",
+ ...actionProps,
+ }}
+ {...uiProps}
+ placeholder={placeholder || "Type something"}
+ onChange={(_, { value }) => {
+ onInputChange(value);
+ }}
+ value={queryString}
+ onKeyPress={onKeyPress}
+ />
+ )
+}
+
+BlrSearchBar.propTypes = {
+ actionProps: PropTypes.object,
+ onBtnSearchClick: PropTypes.func,
+ onInputChange: PropTypes.func,
+ onKeyPress: PropTypes.func,
+ placeholder: PropTypes.string,
+ queryString: PropTypes.string,
+ uiProps: PropTypes.object,
+};
+
+BlrSearchBar.defaultProps = {
+ actionProps: null,
+ onBtnSearchClick: null,
+ onInputChange: null,
+ onKeyPress: null,
+ placeholder: "",
+ queryString: "",
+ uiProps: null,
+};
\ No newline at end of file
diff --git a/site/zenodo_rdm/assets/semantic-ui/js/zenodo_rdm/src/blr-related-works/components/Filter.js b/site/zenodo_rdm/assets/semantic-ui/js/zenodo_rdm/src/blr-related-works/components/Filter.js
new file mode 100644
index 00000000..a6a4a04a
--- /dev/null
+++ b/site/zenodo_rdm/assets/semantic-ui/js/zenodo_rdm/src/blr-related-works/components/Filter.js
@@ -0,0 +1,99 @@
+// This file is part of Zenodo.
+// Copyright (C) 2023 CERN.
+//
+// Zenodo is free software; you can redistribute it
+// and/or modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// Zenodo is distributed in the hope that it will be
+// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Zenodo; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+// MA 02111-1307, USA.
+//
+// In applying this license, CERN does not
+// waive the privileges and immunities granted to it by virtue of its status
+// as an Intergovernmental Organization or submit itself to any jurisdiction.
+
+import React from "react";
+import { PropTypes } from "prop-types";
+import { Dropdown, Label, Button, Icon } from "semantic-ui-react";
+
+export const FilterContainer = ({agg, containerCmp, updateQueryFilters}) => {
+ const clearFacets = () => {
+ if (containerCmp.props.selectedFilters.length) {
+ updateQueryFilters([agg.aggName, ""], containerCmp.props.selectedFilters);
+ }
+ };
+
+ return (
+
+
+ {containerCmp}
+
+
+
+
+
+ );
+}
+
+FilterContainer.propTypes = {
+ agg: PropTypes.object.isRequired,
+ updateQueryFilters: PropTypes.func.isRequired,
+ containerCmp: PropTypes.node.isRequired,
+};
+
+
+export const Filter = ({ valuesCmp }) => {
+ return (
+
+
+ {valuesCmp}
+
+
+ );
+};
+
+Filter.propTypes = {
+ valuesCmp: PropTypes.array.isRequired,
+};
+
+
+export const FilterValues = ({
+ bucket,
+ isSelected,
+ onFilterClicked,
+ label,
+}) => {
+ return (
+ onFilterClicked(bucket.key)}
+ value={bucket.key}
+ className="flex align-items-center justify-space-between"
+ >
+ {isSelected &&
+
+ }
+ {label}
+
+
+ );
+};
+
+FilterValues.propTypes = {
+ bucket: PropTypes.object.isRequired,
+ isSelected: PropTypes.bool.isRequired,
+ onFilterClicked: PropTypes.func.isRequired,
+ label: PropTypes.string.isRequired,
+};
\ No newline at end of file
diff --git a/site/zenodo_rdm/assets/semantic-ui/js/zenodo_rdm/src/blr-related-works/components/LayoutSwitchButtons.js b/site/zenodo_rdm/assets/semantic-ui/js/zenodo_rdm/src/blr-related-works/components/LayoutSwitchButtons.js
new file mode 100644
index 00000000..872b0825
--- /dev/null
+++ b/site/zenodo_rdm/assets/semantic-ui/js/zenodo_rdm/src/blr-related-works/components/LayoutSwitchButtons.js
@@ -0,0 +1,51 @@
+// This file is part of Zenodo.
+// Copyright (C) 2023 CERN.
+//
+// Zenodo is free software; you can redistribute it
+// and/or modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// Zenodo is distributed in the hope that it will be
+// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Zenodo; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+// MA 02111-1307, USA.
+//
+// In applying this license, CERN does not
+// waive the privileges and immunities granted to it by virtue of its status
+// as an Intergovernmental Organization or submit itself to any jurisdiction.
+
+import React from "react";
+import { PropTypes } from "prop-types";
+import { Button } from "semantic-ui-react";
+
+export const LayoutSwitchButtons = ({ currentLayout, onLayoutChange }) => {
+ return (
+
+
+ );
+};
+
+LayoutSwitchButtons.propTypes = {
+ currentLayout: PropTypes.string.isRequired,
+ onLayoutChange: PropTypes.func.isRequired,
+};
\ No newline at end of file
diff --git a/site/zenodo_rdm/assets/semantic-ui/js/zenodo_rdm/src/blr-related-works/components/NoResults.js b/site/zenodo_rdm/assets/semantic-ui/js/zenodo_rdm/src/blr-related-works/components/NoResults.js
new file mode 100644
index 00000000..d0bdb6bc
--- /dev/null
+++ b/site/zenodo_rdm/assets/semantic-ui/js/zenodo_rdm/src/blr-related-works/components/NoResults.js
@@ -0,0 +1,55 @@
+// This file is part of Zenodo.
+// Copyright (C) 2023 CERN.
+//
+// Zenodo is free software; you can redistribute it
+// and/or modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// Zenodo is distributed in the hope that it will be
+// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Zenodo; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+// MA 02111-1307, USA.
+//
+// In applying this license, CERN does not
+// waive the privileges and immunities granted to it by virtue of its status
+// as an Intergovernmental Organization or submit itself to any jurisdiction.
+
+import React from "react";
+import { Container, Icon } from "semantic-ui-react";
+
+export const NoResults = ({ queryString }) => {
+
+ return (
+
+ {queryString && (
+
+
+
+
+ No results found for '{queryString}'
+
+
+
+ ) || (
+
+
+ No related content for this record.
+
+ )}
+
+ )
+}
+
+NoResults.propTypes = {
+ queryString: PropTypes.string,
+};
+
+NoResults.defaultProps = {
+ queryString: "",
+};
\ No newline at end of file
diff --git a/site/zenodo_rdm/assets/semantic-ui/js/zenodo_rdm/src/blr-related-works/components/RecordItem.js b/site/zenodo_rdm/assets/semantic-ui/js/zenodo_rdm/src/blr-related-works/components/RecordItem.js
new file mode 100644
index 00000000..210b64d3
--- /dev/null
+++ b/site/zenodo_rdm/assets/semantic-ui/js/zenodo_rdm/src/blr-related-works/components/RecordItem.js
@@ -0,0 +1,133 @@
+// This file is part of Zenodo.
+// Copyright (C) 2023 CERN.
+//
+// Zenodo is free software; you can redistribute it
+// and/or modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// Zenodo is distributed in the hope that it will be
+// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Zenodo; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+// MA 02111-1307, USA.
+//
+// In applying this license, CERN does not
+// waive the privileges and immunities granted to it by virtue of its status
+// as an Intergovernmental Organization or submit itself to any jurisdiction.
+
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Grid, Header, Item, Label, Icon } from "semantic-ui-react";
+import _get from "lodash/get";
+import { SearchItemCreators } from "@js/invenio_app_rdm/utils";
+import { Image } from "react-invenio-forms";
+
+const layoutProps = (result) => ({
+ accessStatusId: _get(result, "ui.access_status.id", "open"),
+ accessStatus: _get(result, "ui.access_status.title_l10n", "Open"),
+ accessStatusIcon: _get(result, "ui.access_status.icon", "unlock"),
+ createdDate: _get(result, "ui.created_date_l10n_long", "Unknown date"),
+ creators: _get(result, "ui.creators.creators", []).slice(0, 3),
+ resourceType: _get(result, "ui.resource_type.title_l10n", "No resource type"),
+ title: _get(result, "metadata.title", "No title"),
+ link: _get(result, "links.self_html", "#"),
+ typeIcon: result.ui?.resource_type?.id.includes("image") ? "image" : "file alternate",
+})
+
+
+export const RecordGridItem = ({result}) => {
+ const { title, creators, resourceType, typeIcon, link } = layoutProps(result);
+
+ return (
+
+
+
+
+ {title}
+
+
+
+
+
+
+ )
+}
+
+RecordListItem.propTypes = {
+ result: PropTypes.object.isRequired,
+}
\ No newline at end of file
diff --git a/site/zenodo_rdm/assets/semantic-ui/js/zenodo_rdm/src/blr-related-works/components/ResultsLayout.js b/site/zenodo_rdm/assets/semantic-ui/js/zenodo_rdm/src/blr-related-works/components/ResultsLayout.js
new file mode 100644
index 00000000..2b2c3505
--- /dev/null
+++ b/site/zenodo_rdm/assets/semantic-ui/js/zenodo_rdm/src/blr-related-works/components/ResultsLayout.js
@@ -0,0 +1,47 @@
+// This file is part of Zenodo.
+// Copyright (C) 2023 CERN.
+//
+// Zenodo is free software; you can redistribute it
+// and/or modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// Zenodo is distributed in the hope that it will be
+// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Zenodo; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+// MA 02111-1307, USA.
+//
+// In applying this license, CERN does not
+// waive the privileges and immunities granted to it by virtue of its status
+// as an Intergovernmental Organization or submit itself to any jurisdiction.
+
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Grid, Item } from "semantic-ui-react";
+
+
+export const ResultsListLayout = ({results}) => (
+
+ {results}
+
+)
+
+ResultsListLayout.propTypes = {
+ results: PropTypes.array.isRequired,
+}
+
+
+export const ResultsGridLayout = ({results}) => (
+
+ {results}
+
+)
+
+ResultsGridLayout.propTypes = {
+ results: PropTypes.array.isRequired,
+}
\ No newline at end of file
diff --git a/site/zenodo_rdm/assets/semantic-ui/js/zenodo_rdm/src/blr-related-works/index.js b/site/zenodo_rdm/assets/semantic-ui/js/zenodo_rdm/src/blr-related-works/index.js
new file mode 100644
index 00000000..5f977f96
--- /dev/null
+++ b/site/zenodo_rdm/assets/semantic-ui/js/zenodo_rdm/src/blr-related-works/index.js
@@ -0,0 +1,56 @@
+// This file is part of Zenodo.
+// Copyright (C) 2023 CERN.
+//
+// Zenodo is free software; you can redistribute it
+// and/or modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// Zenodo is distributed in the hope that it will be
+// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Zenodo; if not, write to the
+// Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+// MA 02111-1307, USA.
+//
+// In applying this license, CERN does not
+// waive the privileges and immunities granted to it by virtue of its status
+// as an Intergovernmental Organization or submit itself to any jurisdiction.
+
+import React from "react";
+import ReactDOM from "react-dom";
+import { BlrSearch } from "./BlrSearch";
+
+const blrContainer = document.getElementById("blr-search");
+const endpoint = blrContainer.dataset.blrEndpoint;
+const recordDOI = JSON.parse(blrContainer.dataset.recordDoi);
+const resourceType = JSON.parse(blrContainer.dataset.resourceType);
+const communities = JSON.parse(blrContainer.dataset.communities);
+const {
+ default: {
+ slug: communitiesSlug
+ }
+} = communities;
+
+const validTypes = [
+ "Figure",
+ "Taxonomic treatment",
+ "Book chapter",
+ "Journal article",
+ "Drawing",
+];
+
+if (communitiesSlug === "biosyslit" && validTypes.includes(resourceType)) {
+ blrContainer &&
+ ReactDOM.render(
+ ,
+ blrContainer
+ );
+}
diff --git a/site/zenodo_rdm/webpack.py b/site/zenodo_rdm/webpack.py
index 111ced77..5b67b3a4 100644
--- a/site/zenodo_rdm/webpack.py
+++ b/site/zenodo_rdm/webpack.py
@@ -23,6 +23,7 @@
"zenodo-rdm-support": "./js/zenodo_rdm/src/support/support.js",
"zenodo-rdm-citations": "./js/zenodo_rdm/src/citations/index.js",
"zenodo-rdm-communities-carousel": "./js/zenodo_rdm/src/communities-carousel.js",
+ "zenodo-rdm-blr-search": "./js/zenodo_rdm/src/blr-related-works/index.js",
},
dependencies={
"@babel/runtime": "^7.9.0",
diff --git a/templates/semantic-ui/zenodo_rdm/records/detail.html b/templates/semantic-ui/zenodo_rdm/records/detail.html
index 3a4a1754..61191396 100644
--- a/templates/semantic-ui/zenodo_rdm/records/detail.html
+++ b/templates/semantic-ui/zenodo_rdm/records/detail.html
@@ -71,6 +71,17 @@
{{ record.ui.access_status.title_l10n }}
{# Additional details #}
{%- block record_details -%}
+ {# BLR Related works #}
+
+
+
{%- include "zenodo_rdm/records/details/details.html" %}
@@ -83,7 +94,8 @@