mirror of
https://github.com/Cian-H/invenio-theme-iform.git
synced 2025-12-23 05:01:58 +00:00
Merge pull request #142 from utnapischtim/feature/override-search-v2
Feature/override search v2
This commit is contained in:
@@ -0,0 +1,324 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 CERN.
|
||||||
|
* Copyright (C) 2020 Northwestern University.
|
||||||
|
* Copyright (C) 2020 Graz University of Technology
|
||||||
|
*
|
||||||
|
* invenio-theme-tugraz is free software; you can redistribute it
|
||||||
|
* and/or modify it under the terms of the MIT License; see LICENSE
|
||||||
|
* file for more details.
|
||||||
|
*
|
||||||
|
* origin: invenio_app_rdm/search/components.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import { Card, Item, Input, Label, Button, Grid, Checkbox, List, } from "semantic-ui-react";
|
||||||
|
import { BucketAggregation, Toggle } from "react-searchkit";
|
||||||
|
import _ from "lodash";
|
||||||
|
import _truncate from "lodash/truncate";
|
||||||
|
import Overridable from "react-overridable";
|
||||||
|
import { SearchBar } from "@js/invenio_search_ui/components";
|
||||||
|
|
||||||
|
export const RDMRecordResultsListItem = ({ result, index }) => {
|
||||||
|
const description = _.get(result, "metadata.description", "No description");
|
||||||
|
const version = _.get(result, "metadata.version", "");
|
||||||
|
const creators = _.get(result, "metadata.creators", []);
|
||||||
|
const title = _.get(result, "metadata.title", "No title");
|
||||||
|
const subjects = _.get(result, "metadata.subjects", null);
|
||||||
|
const rights = _.get(result, "metadata.rights", null)
|
||||||
|
|
||||||
|
const publicationDate = _.get(result, "ui.publication_date_l10n_long", "No publication date found");
|
||||||
|
const createdDate = _.get(result, "ui.created_date_l10n_long", "No creation date found.");
|
||||||
|
const resourceType = _.get(result, "ui.resource_type", "No resource type");
|
||||||
|
const access = _.get(result, "ui.access_right.title", "No access rights");
|
||||||
|
const accessRightCategory = _.get(result, "ui.access_right.category", "closed");
|
||||||
|
const accessRightIcon = _.get(result, "ui.access_right.icon", "closed");
|
||||||
|
const accessRight = {type: access, category: accessRightCategory, icon: accessRightIcon, rights};
|
||||||
|
|
||||||
|
const href = `/records/${result.id}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Item key={index}>
|
||||||
|
<Item.Content>
|
||||||
|
<div className="badges">
|
||||||
|
<Label className="record-version">
|
||||||
|
{publicationDate} {version ? `(${version})` : null}
|
||||||
|
</Label>
|
||||||
|
<Label className="teal">
|
||||||
|
{resourceType}
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
|
<Item.Header href={href}>{title}</Item.Header>
|
||||||
|
<Creators creators={creators}/>
|
||||||
|
<Item.Description href={href}>
|
||||||
|
{_truncate(description.replace(/(<([^>]+)>)/ig, ''), { length: 350 })}
|
||||||
|
</Item.Description>
|
||||||
|
<Footer subjects={subjects} createdDate={createdDate} accessRight={accessRight}/>
|
||||||
|
</Item.Content>
|
||||||
|
</Item>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Creators = ({creators}) => {
|
||||||
|
const creatorTags = creators.map((creator, index) => {
|
||||||
|
return <Creator key={index} creator={creator}/>;
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="creators">
|
||||||
|
{creatorTags}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Identifiers = ({creator}) => {
|
||||||
|
return (
|
||||||
|
<div className="identifiers">
|
||||||
|
{_.isObject(creator.identifiers) && creator.identifiers.hasOwnProperty("orcid") &&
|
||||||
|
<Orcid creator={creator}/>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Orcid = ({creator}) => {
|
||||||
|
const href = `https://orcid.org/${creator.identifiers.orcid}`
|
||||||
|
|
||||||
|
return (
|
||||||
|
<a href={href} target="_blank">
|
||||||
|
<img className="inline-orcid" src="/static/extra/orcid.png"/>
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Creator = ({creator}) => {
|
||||||
|
return (
|
||||||
|
<div className="creator">
|
||||||
|
<Identifiers creator={creator}/>
|
||||||
|
<span className="text-muted">{creator.name}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const Footer = ({subjects, createdDate, accessRight}) => {
|
||||||
|
return (
|
||||||
|
<Item.Extra>
|
||||||
|
<div className="left floated column">
|
||||||
|
{subjects && subjects.map((subject, index) => (
|
||||||
|
<Label key={index} size="tiny">
|
||||||
|
{subject.subject}
|
||||||
|
</Label>
|
||||||
|
))}
|
||||||
|
{createdDate && (
|
||||||
|
<div>
|
||||||
|
<small>
|
||||||
|
Uploaded on <span>{createdDate}</span>
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="right floated column">
|
||||||
|
<span className={`ui access-right ${accessRight.category}`}>
|
||||||
|
<i className={`icon ${accessRight.icon}`}></i>
|
||||||
|
{accessRight.type} {accessRight.rights && accessRight.rights.map((right, index) => (
|
||||||
|
<a key={index} href={right.uri}>({right.identifier})</a>
|
||||||
|
))}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</Item.Extra>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ATTENTION:
|
||||||
|
* The following classes are only here because it is not easily possible to
|
||||||
|
* import it from the original module.
|
||||||
|
* If there is in the future a possibility to import following classes from
|
||||||
|
* invenio_app_rdm then this should be done!
|
||||||
|
*/
|
||||||
|
export const RDMRecordResultsGridItem = ({ result, index }) => {
|
||||||
|
const description = _.get(result, "metadata.description", "No description");
|
||||||
|
return (
|
||||||
|
<Card fluid key={index} href={`/records/${result.pid}`}>
|
||||||
|
<Card.Content>
|
||||||
|
<Card.Header>{result.metadata.title}</Card.Header>
|
||||||
|
<Card.Description>
|
||||||
|
{_truncate(description, { length: 200 })}
|
||||||
|
</Card.Description>
|
||||||
|
</Card.Content>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RDMRecordSearchBarContainer = () => {
|
||||||
|
return (
|
||||||
|
<Overridable id={"SearchApp.searchbar"}>
|
||||||
|
<SearchBar />
|
||||||
|
</Overridable>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RDMRecordSearchBarElement = ({
|
||||||
|
placeholder: passedPlaceholder,
|
||||||
|
queryString,
|
||||||
|
onInputChange,
|
||||||
|
executeSearch,
|
||||||
|
}) => {
|
||||||
|
const placeholder = passedPlaceholder || "Search";
|
||||||
|
const onBtnSearchClick = () => {
|
||||||
|
executeSearch();
|
||||||
|
};
|
||||||
|
const onKeyPress = (event) => {
|
||||||
|
if (event.key === "Enter") {
|
||||||
|
executeSearch();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<Input
|
||||||
|
action={{
|
||||||
|
icon: "search",
|
||||||
|
onClick: onBtnSearchClick,
|
||||||
|
className: "search",
|
||||||
|
}}
|
||||||
|
placeholder={placeholder}
|
||||||
|
onChange={(event, { value }) => {
|
||||||
|
onInputChange(value);
|
||||||
|
}}
|
||||||
|
value={queryString}
|
||||||
|
onKeyPress={onKeyPress}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RDMRecordFacetsValues = ({
|
||||||
|
bucket,
|
||||||
|
isSelected,
|
||||||
|
onFilterClicked,
|
||||||
|
getChildAggCmps,
|
||||||
|
}) => {
|
||||||
|
const childAggCmps = getChildAggCmps(bucket);
|
||||||
|
const [isActive, setisActive] = useState(false);
|
||||||
|
const hasChildren = childAggCmps && childAggCmps.props.buckets.length > 0;
|
||||||
|
return (
|
||||||
|
<List.Item key={bucket.key}>
|
||||||
|
<div
|
||||||
|
className={`title ${hasChildren ? "" : "facet-subtitle"} ${
|
||||||
|
isActive ? "active" : ""
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<List.Content floated="right">
|
||||||
|
<Label circular>{bucket.doc_count}</Label>
|
||||||
|
</List.Content>
|
||||||
|
{hasChildren ? (
|
||||||
|
<i
|
||||||
|
className={`angle ${isActive ? "down" : "right"} icon`}
|
||||||
|
onClick={() => setisActive(!isActive)}
|
||||||
|
></i>
|
||||||
|
) : null}
|
||||||
|
<Checkbox
|
||||||
|
label={bucket.label}
|
||||||
|
value={bucket.key}
|
||||||
|
onClick={() => onFilterClicked(bucket.key)}
|
||||||
|
checked={isSelected}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={`content facet-content ${isActive ? "active" : ""}`}>
|
||||||
|
{childAggCmps}
|
||||||
|
</div>
|
||||||
|
</List.Item>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const SearchHelpLinks = () => {
|
||||||
|
return (
|
||||||
|
<Overridable id={"RdmSearch.SearchHelpLinks"}>
|
||||||
|
<Grid className="padded-small">
|
||||||
|
<Grid.Row className="no-padded">
|
||||||
|
<Grid.Column>
|
||||||
|
<Card className="borderless-facet">
|
||||||
|
<Card.Content>
|
||||||
|
<a>Advanced search</a>
|
||||||
|
</Card.Content>
|
||||||
|
</Card>
|
||||||
|
</Grid.Column>
|
||||||
|
</Grid.Row>
|
||||||
|
<Grid.Row className="no-padded">
|
||||||
|
<Grid.Column>
|
||||||
|
<Card className="borderless-facet">
|
||||||
|
<Card.Content>
|
||||||
|
<a>Search guide</a>
|
||||||
|
</Card.Content>
|
||||||
|
</Card>
|
||||||
|
</Grid.Column>
|
||||||
|
</Grid.Row>
|
||||||
|
</Grid>
|
||||||
|
</Overridable>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RDMRecordFacets = ({ aggs, currentResultsState }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Toggle
|
||||||
|
title="Versions"
|
||||||
|
label="View all versions"
|
||||||
|
filterValue={["all_versions", "true"]}
|
||||||
|
/>
|
||||||
|
{aggs.map((agg) => {
|
||||||
|
return (
|
||||||
|
<div key={agg.title} className="ui accordion">
|
||||||
|
<BucketAggregation title={agg.title} agg={agg} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
<SearchHelpLinks />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RDMBucketAggregationElement = ({ title, containerCmp }) => {
|
||||||
|
return (
|
||||||
|
<Card className="borderless-facet">
|
||||||
|
<Card.Content>
|
||||||
|
<Card.Header>{title}</Card.Header>
|
||||||
|
</Card.Content>
|
||||||
|
<Card.Content>{containerCmp}</Card.Content>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RDMToggleComponent = ({
|
||||||
|
updateQueryFilters,
|
||||||
|
userSelectionFilters,
|
||||||
|
filterValue,
|
||||||
|
label,
|
||||||
|
title,
|
||||||
|
isChecked,
|
||||||
|
}) => {
|
||||||
|
const _isChecked = (userSelectionFilters) => {
|
||||||
|
const isFilterActive =
|
||||||
|
userSelectionFilters.filter((filter) => filter[0] === filterValue[0])
|
||||||
|
.length > 0;
|
||||||
|
return isFilterActive;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onToggleClicked = () => {
|
||||||
|
updateQueryFilters(filterValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
var isChecked = _isChecked(userSelectionFilters);
|
||||||
|
return (
|
||||||
|
<Card className="borderless-facet">
|
||||||
|
<Card.Content>
|
||||||
|
<Card.Header>{title}</Card.Header>
|
||||||
|
</Card.Content>
|
||||||
|
<Card.Content>
|
||||||
|
<Checkbox
|
||||||
|
toggle
|
||||||
|
label={label}
|
||||||
|
onClick={onToggleClicked}
|
||||||
|
checked={isChecked}
|
||||||
|
/>
|
||||||
|
</Card.Content>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Graz University of Technology
|
||||||
|
*
|
||||||
|
* invenio-theme-tugraz is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT License; see LICENSE file for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import { defaultComponents, createSearchAppInit } from "@js/invenio_search_ui";
|
||||||
|
import {
|
||||||
|
RDMRecordResultsListItem,
|
||||||
|
RDMBucketAggregationElement,
|
||||||
|
RDMRecordFacets,
|
||||||
|
RDMRecordFacetsValues,
|
||||||
|
RDMRecordResultsGridItem,
|
||||||
|
RDMRecordSearchBarContainer,
|
||||||
|
RDMRecordSearchBarElement,
|
||||||
|
RDMToggleComponent,
|
||||||
|
} from "./components";
|
||||||
|
|
||||||
|
const initSearchApp = createSearchAppInit({
|
||||||
|
"ResultsList.item": RDMRecordResultsListItem,
|
||||||
|
"BucketAggregation.element": RDMBucketAggregationElement,
|
||||||
|
"BucketAggregationValues.element": RDMRecordFacetsValues,
|
||||||
|
"ResultsGrid.item": RDMRecordResultsGridItem,
|
||||||
|
"SearchApp.facets": RDMRecordFacets,
|
||||||
|
"SearchApp.searchbarContainer": RDMRecordSearchBarContainer,
|
||||||
|
"SearchBar.element": RDMRecordSearchBarElement,
|
||||||
|
"SearchFilters.ToggleComponent": RDMToggleComponent,
|
||||||
|
});
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2020 Graz University of Technology
|
||||||
|
*
|
||||||
|
* invenio-theme-tugraz is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the MIT License; see LICENSE file for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.creators {
|
||||||
|
margin: 0 0 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.creators span {
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.creator:not(:last-child):after {
|
||||||
|
color: #777;
|
||||||
|
content: ';';
|
||||||
|
}
|
||||||
|
|
||||||
|
.creator {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.identifiers {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ui.access-right {
|
||||||
|
color: #FFFFFF;
|
||||||
|
|
||||||
|
&.open {
|
||||||
|
color: @accessRightOpen;
|
||||||
|
}
|
||||||
|
&.restricted {
|
||||||
|
color: @accessRightRestricted;
|
||||||
|
}
|
||||||
|
&.embargoed {
|
||||||
|
color: @accessRightEmbargoed;
|
||||||
|
}
|
||||||
|
&.closed {
|
||||||
|
color: @accessRightClosed;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
@import "overrides";
|
@import "overrides";
|
||||||
@import "frontpage";
|
@import "frontpage";
|
||||||
@import "record";
|
@import "record";
|
||||||
|
@import "search";
|
||||||
@import "macros";
|
@import "macros";
|
||||||
@import "login";
|
@import "login";
|
||||||
@import (css)
|
@import (css)
|
||||||
|
|||||||
@@ -15,3 +15,8 @@
|
|||||||
|
|
||||||
//@navbar_background_image: unset;
|
//@navbar_background_image: unset;
|
||||||
//@navbar_background_color: #ffffff;
|
//@navbar_background_color: #ffffff;
|
||||||
|
|
||||||
|
@accessRightOpen: #e9711c;
|
||||||
|
@accessRightRestricted: #fbbd08;
|
||||||
|
@accessRightEmbargoed: #db2828;
|
||||||
|
@accessRightClosed: #db2828;
|
||||||
|
|||||||
@@ -94,3 +94,9 @@ DEPOSITS_HEADER_TEMPLATE = "invenio_theme_tugraz/header.html"
|
|||||||
# template="invenio_theme_tugraz/record_landing_page.html"
|
# template="invenio_theme_tugraz/record_landing_page.html"
|
||||||
# )
|
# )
|
||||||
"""override the default record landing page"""
|
"""override the default record landing page"""
|
||||||
|
|
||||||
|
# Invenio-search-ui
|
||||||
|
# =============
|
||||||
|
# See https://invenio-search-ui.readthedocs.io/en/latest/configuration.html
|
||||||
|
SEARCH_UI_SEARCH_TEMPLATE = "invenio_theme_tugraz/search.html"
|
||||||
|
"""override the default search page"""
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
{#
|
||||||
|
Copyright (C) 2020 Graz University of Technology
|
||||||
|
|
||||||
|
invenio-theme-tugraz is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the MIT License; see LICENSE file for more details.
|
||||||
|
#}
|
||||||
|
{%- extends config.BASE_TEMPLATE %}
|
||||||
|
|
||||||
|
{%- block javascript %}
|
||||||
|
{{ super() }}
|
||||||
|
{{ webpack['invenio-theme-tugraz-search-app.js'] }}
|
||||||
|
{%- endblock %}
|
||||||
|
|
||||||
|
{%- block page_body %}
|
||||||
|
|
||||||
|
<div data-invenio-search-config='{
|
||||||
|
"aggs": [
|
||||||
|
{
|
||||||
|
"aggName": "access_right",
|
||||||
|
"field": "access_right",
|
||||||
|
"title": "Access Right"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"aggName": "resource_type",
|
||||||
|
"field": "resource_type.type",
|
||||||
|
"title": "Resource Type",
|
||||||
|
"childAgg": {
|
||||||
|
"aggName": "subtype",
|
||||||
|
"field": "resource_type.subtype",
|
||||||
|
"title": "Resource Type"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"appId": "rdm-search",
|
||||||
|
"initialQueryState": {
|
||||||
|
"hiddenParams": null,
|
||||||
|
"size": 10
|
||||||
|
},
|
||||||
|
"layoutOptions": {
|
||||||
|
"gridView": false,
|
||||||
|
"listView": true
|
||||||
|
},
|
||||||
|
"paginationOptions": {
|
||||||
|
"defaultValue": 10,
|
||||||
|
"resultsPerPage": [
|
||||||
|
{
|
||||||
|
"text": "10",
|
||||||
|
"value": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "20",
|
||||||
|
"value": 20
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "50",
|
||||||
|
"value": 50
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"searchApi": {
|
||||||
|
"axios": {
|
||||||
|
"headers": {
|
||||||
|
"Accept": "application/vnd.inveniordm.v1+json"
|
||||||
|
},
|
||||||
|
"url": "/api/records",
|
||||||
|
"withCredentials": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sortOrderDisabled": true,
|
||||||
|
"sortOptions": [
|
||||||
|
{
|
||||||
|
"sortBy": "bestmatch",
|
||||||
|
"text": "Best match"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"sortBy": "newest",
|
||||||
|
"text": "Newest"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}'></div>
|
||||||
|
|
||||||
|
{%- endblock page_body %}
|
||||||
@@ -17,6 +17,7 @@ theme = WebpackThemeBundle(
|
|||||||
entry={
|
entry={
|
||||||
"invenio-theme-tugraz-theme": "./less/invenio_theme_tugraz/theme.less",
|
"invenio-theme-tugraz-theme": "./less/invenio_theme_tugraz/theme.less",
|
||||||
"invenio-theme-tugraz-js": "./js/invenio_theme_tugraz/theme.js",
|
"invenio-theme-tugraz-js": "./js/invenio_theme_tugraz/theme.js",
|
||||||
|
"invenio-theme-tugraz-search-app": "./js/invenio_theme_tugraz/search/index.js",
|
||||||
},
|
},
|
||||||
dependencies={},
|
dependencies={},
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user