From e2d914de8d61aabbbce538f57abeff6a1fb5703a Mon Sep 17 00:00:00 2001 From: Andrej Fast Date: Tue, 1 Aug 2023 17:07:29 +0200 Subject: [PATCH 01/41] [WIP] Usage of the new databrowser-API inside the web-databrowser. --- .../js/Components/AccordionItemBody/index.js | 2 +- assets/js/Components/Pagination.js | 77 +++++++++++++++++++ .../js/Containers/Databrowser/FacetPanel.js | 71 +++++++++++++++++ .../js/Containers/Databrowser/FilesPanel.js | 27 ++++++- assets/js/Containers/Databrowser/actions.js | 54 +++++++++---- assets/js/Containers/Databrowser/constants.js | 7 ++ assets/js/Containers/Databrowser/reducers.js | 15 +++- solr/urls.py | 11 +++ solr/views.py | 32 +++++++- 9 files changed, 272 insertions(+), 24 deletions(-) create mode 100644 assets/js/Components/Pagination.js create mode 100644 assets/js/Containers/Databrowser/FacetPanel.js diff --git a/assets/js/Components/AccordionItemBody/index.js b/assets/js/Components/AccordionItemBody/index.js index b9691655..1d83a973 100644 --- a/assets/js/Components/AccordionItemBody/index.js +++ b/assets/js/Components/AccordionItemBody/index.js @@ -45,7 +45,7 @@ Row.propTypes = { data: PropTypes.array, rowData: PropTypes.shape({ value: PropTypes.string, - count: PropTypes.number, + count: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), }), elemRef: PropTypes.oneOfType([ PropTypes.func, diff --git a/assets/js/Components/Pagination.js b/assets/js/Components/Pagination.js new file mode 100644 index 00000000..edcbfee7 --- /dev/null +++ b/assets/js/Components/Pagination.js @@ -0,0 +1,77 @@ +import React, { useEffect, useState } from "react"; +import PropTypes from "prop-types"; +import { Button, Form } from "react-bootstrap"; + +import { FaChevronLeft, FaChevronRight } from "react-icons/fa"; + +export default function Pagination(props) { + const [active, setActive] = useState(props.active); + + useEffect(() => { + setActive(props.active); + }, [props]); + + function handleFieldChange(e) { + setActive(Number(e.target.value)); + } + + function getPrevious() { + return props.handleSubmit(props.active - 1); + } + + function getNext() { + console.log("next", props.active); + return props.handleSubmit(props.active + 1); + } + + function getPage(e) { + if (e.key === "Enter") { + const valid = active >= 1 && active <= props.items; + if (valid) { + props.handleSubmit(active); + } + } + } + + const firstPage = 1; + const lastPage = props.items; + const activePage = props.active; + const valid = active >= 1 && active <= lastPage; + console.log("ACTIVE", active); + return ( +
+ {" "} + + {" "} + + of {lastPage} + +
+ ); +} + +Pagination.propTypes = { + items: PropTypes.number.isRequired, + active: PropTypes.number.isRequired, + handleSubmit: PropTypes.func.isRequired, +}; diff --git a/assets/js/Containers/Databrowser/FacetPanel.js b/assets/js/Containers/Databrowser/FacetPanel.js new file mode 100644 index 00000000..122d943c --- /dev/null +++ b/assets/js/Containers/Databrowser/FacetPanel.js @@ -0,0 +1,71 @@ +import React from "react"; +import PropTypes from "prop-types"; + +import { Badge } from "react-bootstrap"; + +import { initCap, underscoreToBlank } from "../../utils"; +import AccordionItemBody from "../../Components/AccordionItemBody"; +import OwnPanel from "../../Components/OwnPanel"; + +/* +This class represents a single Dropdown Menu for a single facet including the title of the facet +*/ +export function FacetPanel({ + value, + keyVar, + metadata, + selectedFacets, + facetMapping, + clickFacet, + isFacetCentered, +}) { + const isFacetSelected = !!selectedFacets[keyVar]; + const facetTitle = initCap(underscoreToBlank(facetMapping[keyVar] ?? keyVar)); + let panelHeader; + if (isFacetSelected) { + panelHeader = ( + + {facetTitle}: {selectedFacets[keyVar]} + + ); + } else if (value.length === 2) { + panelHeader = ( + + {facetTitle}: {value[0]} + + ); + } else { + const numberOfValues = value.length / 2; + panelHeader = ( + + {facetTitle} + {numberOfValues} + + ); + } + return ( + this.clickFacet(keyVar) : null} + > + + + ); +} + +FacetPanel.propTypes = { + value: PropTypes.array, + keyVar: PropTypes.string, + metadata: PropTypes.object, + facetMapping: PropTypes.object, + selectedFacets: PropTypes.object, + isFacetCentered: PropTypes.bool, + clickFacet: PropTypes.func.isRequired, +}; diff --git a/assets/js/Containers/Databrowser/FilesPanel.js b/assets/js/Containers/Databrowser/FilesPanel.js index d581566f..a84717e9 100644 --- a/assets/js/Containers/Databrowser/FilesPanel.js +++ b/assets/js/Containers/Databrowser/FilesPanel.js @@ -2,13 +2,17 @@ import React, { useState } from "react"; import PropTypes from "prop-types"; import { connect } from "react-redux"; import { Tooltip, OverlayTrigger, Button, Badge } from "react-bootstrap"; +import { withRouter } from "react-router"; import { FaInfoCircle } from "react-icons/fa"; +import queryString from "query-string"; + import NcdumpDialog, { NcDumpDialogState } from "../../Components/NcdumpDialog"; import CircularSpinner from "../../Components/Spinner"; import { getCookie } from "../../utils"; +import Pagination from "../../Components/Pagination"; function FilesPanelImpl(props) { const { files, numFiles, fileLoading } = props.databrowser; @@ -20,6 +24,15 @@ function FilesPanelImpl(props) { error: null, }); + function setPageOffset(offset) { + const currentLocation = props.location.pathname; + const query = queryString.stringify({ + ...props.location.query, + start: (offset - 1) * 100, + }); + props.router.push(currentLocation + "?" + query); + } + function loadNcdump(fn, pw) { const url = "/api/solr/ncdump/"; setNcDump({ ...ncdump, status: NcDumpDialogState.LOADING }); @@ -40,7 +53,6 @@ function FilesPanelImpl(props) { if (!resp.ok) { /* eslint-disable */ return resp.json().then((json) => { - console.log(resp.statusText); if (json.error_msg) { throw new Error(json.error_msg); } else { @@ -67,12 +79,20 @@ function FilesPanelImpl(props) { } const [filename, setFilename] = useState(null); + return (

Files {numFiles.toLocaleString("en-US")}

+
+ +