Skip to content

Commit

Permalink
landing page: add blr related works section
Browse files Browse the repository at this point in the history
  • Loading branch information
jennur committed Dec 13, 2023
1 parent 36e8aae commit 82d70a3
Show file tree
Hide file tree
Showing 11 changed files with 587 additions and 1 deletion.
5 changes: 5 additions & 0 deletions assets/less/zenodo-rdm/globals/site.overrides
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,9 @@ a.inverted {
.ui.medium.header {
font-size: 1.45rem;
}
}

.image.thumbnail-image {
height: 150px;
object-fit: cover;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// 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 {
ReactSearchKit,
InvenioSearchApi,
ResultsLoader,
ResultsMultiLayout,
Error,
EmptyResults,
Pagination,
BucketAggregation,
LayoutSwitcher,
} from "react-searchkit";
import { OverridableContext } from "react-overridable";
import { apiConfig } from "./api/config";
import { 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 { LayoutSwitchButtons } from "./components/LayoutSwitchButtons";
import { NoResults } from "./components/NoResults";

const blrSearchAppID = "blrSearch";

const overriddenComponents = {
[`${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));

const initialState = {
queryString: queryString(relationType(resourceType), recordDOI),
sortBy: "bestmatch",
sortOrder: "asc",
page: 1,
size: 4,
layout: "list",
};

return (
<>
<Header as="h2">Linked records</Header>
<OverridableContext.Provider value={overriddenComponents}>
<ReactSearchKit
appName={blrSearchAppID}
searchApi={searchApi}
urlHandlerApi={{ enabled: false }}
initialQueryState={initialState}
>
<>
<div className="flex align-items-center justify-space-between">
<BucketAggregation
agg={{ field: "resource_type", aggName: "resource_type" }}
/>
<LayoutSwitcher />
</div>

<Segment>
<ResultsLoader>
<ResultsMultiLayout />
<Error />
<EmptyResults />
</ResultsLoader>
<Container align="center" className="rel-pt-1">
<Pagination options={{ size: "mini", showEllipsis: true }} />
</Container>
</Segment>
</>
</ReactSearchKit>
</OverridableContext.Provider>
</>
);
};

BlrSearch.propTypes = {
endpoint: PropTypes.string.isRequired,
recordDOI: PropTypes.string.isRequired,
resourceType: PropTypes.string.isRequired,
};
Original file line number Diff line number Diff line change
@@ -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",
},
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// 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 (
<div className="flex align-items-center">
<div>{containerCmp}</div>
<div>
<Button onClick={clearFacets} content="Reset filters" />
</div>
</div>
);
};

FilterContainer.propTypes = {
agg: PropTypes.object.isRequired,
updateQueryFilters: PropTypes.func.isRequired,
containerCmp: PropTypes.node.isRequired,
};

export const Filter = ({ valuesCmp }) => {
return (
<Dropdown text="Filter by type" button>
<Dropdown.Menu>{valuesCmp}</Dropdown.Menu>
</Dropdown>
);
};

Filter.propTypes = {
valuesCmp: PropTypes.array.isRequired,
};

export const FilterValues = ({ bucket, isSelected, onFilterClicked, label }) => {
return (
<Dropdown.Item
key={bucket.key}
id={`${bucket.key}-agg-value`}
selected={isSelected}
onClick={() => onFilterClicked(bucket.key)}
value={bucket.key}
className="flex align-items-center justify-space-between"
>
{isSelected && <Icon name="check" className="positive" />}

<span>{label}</span>
<Label size="small" className="rel-ml-1 mr-0">
{bucket.doc_count.toLocaleString("en-US")}
</Label>
</Dropdown.Item>
);
};

FilterValues.propTypes = {
bucket: PropTypes.object.isRequired,
isSelected: PropTypes.bool.isRequired,
onFilterClicked: PropTypes.func.isRequired,
label: PropTypes.string.isRequired,
};
Original file line number Diff line number Diff line change
@@ -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 (
<Button.Group>
<Button
icon="th list"
name="list"
active={currentLayout === "list"}
onClick={() => onLayoutChange("list")}
aria-label="List view"
/>
<Button
icon="th"
name="grid"
active={currentLayout === "grid"}
onClick={() => onLayoutChange("grid")}
aria-label="Grid view"
/>
</Button.Group>
);
};

LayoutSwitchButtons.propTypes = {
currentLayout: PropTypes.string.isRequired,
onLayoutChange: PropTypes.func.isRequired,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// 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 { Container, Icon } from "semantic-ui-react";

export const NoResults = ({ queryString }) => {
return (
<Container align={(!queryString && "left") || "center"}>
{(queryString && (
<div className="text-muted rel-p-1">
<Icon name="search" size="huge" />
<p className="rel-mt-1">
<strong>No results found for '{queryString}'</strong>
</p>
</div>
)) || (
<p>
<Icon name="folder open outline" />
<em>No related content for this record.</em>
</p>
)}
</Container>
);
};

NoResults.propTypes = {
queryString: PropTypes.string,
};

NoResults.defaultProps = {
queryString: "",
};
Loading

0 comments on commit 82d70a3

Please sign in to comment.