Commit 234d32e4 authored by Jacob Cain's avatar Jacob Cain
Browse files

Better pagination and results per page controls

parent fbdfddf5
Loading
Loading
Loading
Loading
+73 −13
Original line number Original line Diff line number Diff line
import React, { useEffect } from "react";
import React, { useEffect } from "react";
import Checkbox from "@mui/material/Checkbox";
import Checkbox from "@mui/material/Checkbox";
import {Card, CardContent, CardActions, Skeleton, Chip, Stack} from "@mui/material";
import {Card, CardContent, CardActions, Skeleton, Chip, Stack, List, ListItemButton, ListItemText, Collapse, Divider, ListSubheader} from "@mui/material";


// icons
// icons
import PreviewIcon from "@mui/icons-material/Preview";
import PreviewIcon from "@mui/icons-material/Preview";
import LaunchIcon from "@mui/icons-material/Launch";
import LaunchIcon from "@mui/icons-material/Launch";
import OpenInFullIcon from "@mui/icons-material/OpenInFull";
import OpenInFullIcon from "@mui/icons-material/OpenInFull";
import CloseFullscreenIcon from "@mui/icons-material/CloseFullscreen";
import CloseFullscreenIcon from "@mui/icons-material/CloseFullscreen";
import TravelExploreIcon from '@mui/icons-material/TravelExplore'; // Footprints.]
import TravelExploreIcon from '@mui/icons-material/TravelExplore'; // Footprints

import ExpandLess from '@mui/icons-material/ExpandLess';
import ExpandMore from '@mui/icons-material/ExpandMore';


// geotiff thumbnail viewer... Should we be using DisplayGeoTiff.jsx instead?
// geotiff thumbnail viewer... Should we be using DisplayGeoTiff.jsx instead?
import GeoTiffViewer from "../../js/geoTiffViewer.js";
import GeoTiffViewer from "../../js/geoTiffViewer.js";
import { lineHeight } from "@mui/system";




/**
/**
@@ -20,7 +24,7 @@ function LoadingFootprints() {
  
  
  return (
  return (
    <div className="resultsList">
    <div className="resultsList">
      { Array(5).fill(null).map((_, i) => (
      { Array(8).fill(null).map((_, i) => (
        <Card sx={{ width: 250, margin: 1}} key={i}>
        <Card sx={{ width: 250, margin: 1}} key={i}>
          <CardContent sx={{padding: 0.9, paddingBottom: 0}}>
          <CardContent sx={{padding: 0.9, paddingBottom: 0}}>
            <div className="resultContainer">
            <div className="resultContainer">
@@ -31,8 +35,6 @@ function LoadingFootprints() {
                <Skeleton/>
                <Skeleton/>
                <Skeleton/>
                <Skeleton/>
                <Skeleton/>
                <Skeleton/>
                <Skeleton/>
                <Skeleton/>
              </div>
              </div>
            </div>
            </div>
          </CardContent>
          </CardContent>
@@ -158,6 +160,18 @@ export default function FootprintResults(props) {
  const [isLoading, setIsLoading] = React.useState(true);
  const [isLoading, setIsLoading] = React.useState(true);
  const [hasFootprints, setHasFootprints] = React.useState(true);
  const [hasFootprints, setHasFootprints] = React.useState(true);


  const [openCollection, setOpenCollection] = React.useState([]);

  function handleOpenCollection(index){
    const nextOpenCollection = openCollection.map((isOpen, curIndex) => {
      if (index === curIndex) {
        return !isOpen;
      }
      return isOpen;
    });
    setOpenCollection(nextOpenCollection);
  }

  useEffect(() => {
  useEffect(() => {


    // If target has collections (of footprints)
    // If target has collections (of footprints)
@@ -253,6 +267,17 @@ export default function FootprintResults(props) {
        setHasFootprints(myFeatureCollections.length > 0);
        setHasFootprints(myFeatureCollections.length > 0);
        setIsLoading(false);
        setIsLoading(false);



        if(myFeatureCollections.length > openCollection.length){
          setOpenCollection(Array(myFeatureCollections.length).fill(true));
        }        

        let myMaxFootprintsMatched = 0;
        for(const collection of myFeatureCollections) {
          myMaxFootprintsMatched = Math.max(myMaxFootprintsMatched, collection.numberMatched);
        }
        props.setMaxFootprintsMatched(myMaxFootprintsMatched);

        // Send to Leaflet
        // Send to Leaflet
        window.postMessage(["setFeatureCollections", myFeatureCollections], "*");
        window.postMessage(["setFeatureCollections", myFeatureCollections], "*");
      })();
      })();
@@ -286,14 +311,49 @@ export default function FootprintResults(props) {
        <LoadingFootprints/>
        <LoadingFootprints/>
      : hasFootprints ?   
      : hasFootprints ?   
        <div className="resultsList">
        <div className="resultsList">
          {featureCollections.map((collection) => (
          <List sx={{maxWidth: 265, paddingTop: 0}}>
            {featureCollections.map((collection, collectionIndex) => (
              <React.Fragment key={collection.id}>
              <React.Fragment key={collection.id}>
              {collection.features.length > 0 ? <p style={{maxWidth: 250, paddingLeft: 10, fontSize: "14px", fontWeight: "bold"}}>{collection.title}</p> : null }
                {collection.features.length > 0 ? 
                <>
                  <ListItemButton onClick={() => handleOpenCollection(collectionIndex)}>
                    <ListItemText
                      sx={{marginTop: 0, marginBottom: 0}}
                      primary={
                        <p style={{fontSize: "12px", lineHeight: "15px", fontWeight: "bold", marginTop: 1, marginBottom: 1}}>{collection.title}</p>
                      } secondary={
                        <span className="collectionStatExpander">
                          <span>{
                            ((props.currentPage-1)*props.currentStep + 1) + "-"
                            + ((props.currentPage-1)*props.currentStep + collection.numberReturned)
                            + " of " + collection.numberMatched + " footprints"}</span>
                          <span className="flatExpander">{openCollection[collectionIndex] ? <ExpandLess /> : <ExpandMore />}</span>
                        </span>
                      }/>
                  </ListItemButton>
                  <Collapse in={openCollection[collectionIndex]}>
                    <Divider/>
                    <ListSubheader sx={{
                        overflow:"hidden", 
                        whiteSpace:"nowrap", 
                        backgroundColor:"rgb(248, 249, 250) none repeat scroll 0% 0%",
                        fontSize: "12px",
                        lineHeight: "24px",
                        borderBottom: "1px solid lightgrey",
                        boxShadow: "0px 1px 2px lightgrey"
                      }}>
                        {collection.title}
                      </ListSubheader>
                    {collection.features.map((feature) => (
                    {collection.features.map((feature) => (
                      <FootprintCard feature={feature} title={collection.title} key={feature.id}/>
                      <FootprintCard feature={feature} title={collection.title} key={feature.id}/>
                    ))}
                    ))}
                  </Collapse>
                </>
                : null }
                <Divider/>
              </React.Fragment>
              </React.Fragment>
            ))}
            ))}
          </List>
        </div>
        </div>
      :
      :
        <NoFootprints/>
        <NoFootprints/>
+42 −50
Original line number Original line Diff line number Diff line
@@ -22,17 +22,9 @@ import FormControl from "@mui/material/FormControl";
import ArrowDownwardIcon from "@mui/icons-material/ArrowDownward";
import ArrowDownwardIcon from "@mui/icons-material/ArrowDownward";
import ArrowUpwardIcon from "@mui/icons-material/ArrowUpward";
import ArrowUpwardIcon from "@mui/icons-material/ArrowUpward";
// No. of Footprints, pagination
// No. of Footprints, pagination
import Slider from "@mui/material/Slider";
import Pagination from "@mui/material/Pagination";
import Pagination from "@mui/material/Pagination";


import {
import { FormHelperText } from "@mui/material";
  getMaxNumberPages,
  setCurrentPage,
  getCurrentPage,
  getNumberMatched,
  setLimit,
  getNumberReturned,
} from "../../js/ApiJsonCollection";


/**
/**
 * Controls css styling for this component using js to css
 * Controls css styling for this component using js to css
@@ -74,6 +66,12 @@ let css = {
    color: "#343a40",
    color: "#343a40",
    fontSize: 18,
    fontSize: 18,
    fontWeight: 600,
    fontWeight: 600,
  },
  thinSelect: {
    marginRight: "12px",
    "& .MuiInputBase-input": {
      padding: "2px 32px 2px 8px",
    }
  }
  }
};
};


@@ -106,14 +104,9 @@ export default function SearchAndFilterInput(props) {
  const [dateFromVal, setDateFromVal] = React.useState(null);     // From Date
  const [dateFromVal, setDateFromVal] = React.useState(null);     // From Date
  const [dateToVal, setDateToVal] = React.useState(null);         // To Date
  const [dateToVal, setDateToVal] = React.useState(null);         // To Date


  // Page Number
  const [pageNumber, setPageNumber] = React.useState(1);

  // Pagination
  // Pagination
  const [maxPages, setMaxPages] = React.useState(10);
  const [maxNumberFootprints, setMaxNumberFootprints] = React.useState(10);
  const [maxNumberFootprints, setMaxNumberFootprints] = React.useState(10);
  const [numberReturned, setNumberReturned] = React.useState(10);
  const [numberReturned, setNumberReturned] = React.useState(10);
  const [limitVal, setLimitVal] = React.useState(10);  // Max Number of footprints requested per collection


  // const handleApply = () => {
  // const handleApply = () => {
  //   setTimeout(() => {
  //   setTimeout(() => {
@@ -133,8 +126,8 @@ export default function SearchAndFilterInput(props) {
    setDateCheckVal(false);
    setDateCheckVal(false);
    setDateFromVal(null);
    setDateFromVal(null);
    setDateToVal(null);
    setDateToVal(null);
    setLimitVal(10);
    props.setCurrentStep(10);
    setMaxPages(1);
    //setMaxPages(1);
    setMaxNumberFootprints(0);
    setMaxNumberFootprints(0);
    setNumberReturned(0);
    setNumberReturned(0);
    //// Uncomment to close details on clear
    //// Uncomment to close details on clear
@@ -146,10 +139,10 @@ export default function SearchAndFilterInput(props) {
    let myQueryString = "?";
    let myQueryString = "?";


    // Page Number
    // Page Number
    if (pageNumber != 1) myQueryString += "page=" + pageNumber + "&";
    if (props.currentPage != 1) myQueryString += "page=" + props.currentPage + "&";
  
  
    // Number of footprints requested per request
    // Number of footprints requested per request
    if (limitVal != 10) myQueryString += "limit=" + limitVal + "&"
    if (props.currentStep != 10) myQueryString += "limit=" + props.currentStep + "&"
    
    
    // Date
    // Date
    if (dateCheckVal) {
    if (dateCheckVal) {
@@ -249,33 +242,24 @@ export default function SearchAndFilterInput(props) {


  // limit
  // limit
  const handleLimitChange = (event, value) => {
  const handleLimitChange = (event, value) => {
    setLimitVal(value);
    props.setCurrentStep(value.props.value);
    setLimit(value);
  };
  };


  // Pagination
  // Pagination
  const handlePageChange = (event, value) => {
  const handlePageChange = (event, value) => {
    setPageNumber(value);
    props.setCurrentPage(value);
    setCurrentPage(value);
  };
  };


  // resets pagination and limit when switching targets
  // resets pagination and limit when switching targets
  useEffect(() => {
  useEffect(() => {
    setTimeout(() => {
    props.setCurrentPage(1);
      setCurrentPage(1);
    props.setCurrentStep(10);
      setPageNumber(1);
      setMaxNumberFootprints(getNumberMatched);
      setNumberReturned(getNumberReturned);
      setLimitVal(10);
      setLimit(10);
      setMaxPages(getMaxNumberPages);
    }, 2000);
  }, [props.target.name]);
  }, [props.target.name]);


  // Listen for any state change (input) and update the query string based on it
  // Listen for any state change (input) and update the query string based on it
  useEffect(() => {
  useEffect(() => {
    buildQueryString();
    buildQueryString();
  }, [sortVal, sortAscCheckVal, areaCheckVal, areaTextVal, keywordCheckVal, keywordTextVal, dateCheckVal, dateFromVal, dateToVal, limitVal, pageNumber]);
  }, [sortVal, sortAscCheckVal, areaCheckVal, areaTextVal, keywordCheckVal, keywordTextVal, dateCheckVal, dateFromVal, dateToVal, props.currentStep, props.currentPage]);


  const onBoxDraw = event => {
  const onBoxDraw = event => {
    console.info("Window meassage received from: ", event.origin);  // For production, check if messages coming from prod url
    console.info("Window meassage received from: ", event.origin);  // For production, check if messages coming from prod url
@@ -462,35 +446,43 @@ export default function SearchAndFilterInput(props) {
      </div>
      </div>
      <div className="panelSectionHeader">
      <div className="panelSectionHeader">
        <div className="panelItem">
        <div className="panelItem">
          <div className="panelSectionTitle">
          <FormControl>
            Number of Displayed Footprints
            <Select
          </div>
              sx={css.thinSelect}
          <Slider
            id="valueSlider"
              size="small"
              size="small"
            valueLabelDisplay="auto"
              value={props.currentStep}
              onChange={handleLimitChange}
              onChange={handleLimitChange}
            value={limitVal}
              >
            max={100}
              <MenuItem value={1}>1</MenuItem>
            defaultValue={10}
              <MenuItem value={2}>2</MenuItem>
          />
              <MenuItem value={5}>5</MenuItem>
              <MenuItem value={10}>10</MenuItem>
              <MenuItem value={15}>15</MenuItem>
              <MenuItem value={20}>20</MenuItem>
              <MenuItem value={30}>30</MenuItem>
              <MenuItem value={50}>50</MenuItem>
              <MenuItem value={100}>100</MenuItem>
            </Select>
            {/* <FormHelperText>Footprints per Request</FormHelperText> */}
          </FormControl>
          <span style={{lineHeight: "28px"}}>
            Footprints per Request
          </span>
        </div>
        </div>
      </div>
      </div>
      <div className="panelSectionHeader">
      <div className="panelSectionHeader">
        <div className="panelItem">
        <div className="panelItem">
          <Pagination
          <Pagination
            id="pagination"
            id="pagination"
            count={maxPages}
            page={props.currentPage}
            count={Math.ceil(props.maxFootprintsMatched/props.currentStep)}
            size="small"
            size="small"
            shape="rounded"
            variant="outlined"
            onChange={handlePageChange}
            onChange={handlePageChange}
          />
          />
        </div>
        </div>
      </div>
      </div>
      <div className="panelSectionHeader">
        <div className="panelItem">
          Displaying {numberReturned} of {maxNumberFootprints} Results
        </div>
      </div>
    </div>
    </div>
  );
  );
}
}
+25 −11
Original line number Original line Diff line number Diff line
@@ -35,18 +35,14 @@ const css = {
 * @component
 * @component
 */
 */
export default function Sidebar(props) {
export default function Sidebar(props) {
  const footprintResultPortalNode = React.useMemo(
    () =>
      createHtmlPortalNode({
        attributes: {
          style: "min-height: 0; display: flex;",
        },
      }),
    []
  );
  
  
  const [showSidePanel, setShowSidePanel] = React.useState(true);
  // Page Tracking
  const [currentStep, setCurrentStep] = React.useState(10);
  const [currentPage, setCurrentPage] = React.useState(1);
  const [maxFootprintsMatched, setMaxFootprintsMatched] = React.useState(10);


  // Layout
  const [showSidePanel, setShowSidePanel] = React.useState(true);
  const [expandResults, setExpandResults] = React.useState(true);
  const [expandResults, setExpandResults] = React.useState(true);
 
 
  const showHideSort = () => {
  const showHideSort = () => {
@@ -57,6 +53,16 @@ export default function Sidebar(props) {
    setExpandResults((expandResults) => !expandResults);
    setExpandResults((expandResults) => !expandResults);
  };
  };


  const footprintResultPortalNode = React.useMemo(
    () =>
      createHtmlPortalNode({
        attributes: {
          style: "min-height: 0; display: flex;",
        },
      }),
    []
  );

  return (
  return (
    <>
    <>
      <div id="right-bar" className="scroll-parent">
      <div id="right-bar" className="scroll-parent">
@@ -72,6 +78,11 @@ export default function Sidebar(props) {
          <SearchAndFilterInput
          <SearchAndFilterInput
            target={props.target}
            target={props.target}
            setQueryString={props.setQueryString}
            setQueryString={props.setQueryString}
            currentStep={currentStep}
            setCurrentStep={setCurrentStep}
            currentPage={currentPage}
            setCurrentPage={setCurrentPage}
            maxFootprintsMatched={maxFootprintsMatched}
          />
          />
          {!expandResults && <OutPortal node={footprintResultPortalNode} />}
          {!expandResults && <OutPortal node={footprintResultPortalNode} />}
        </div>
        </div>
@@ -85,6 +96,9 @@ export default function Sidebar(props) {
          queryString={props.queryString} 
          queryString={props.queryString} 
          changeLayout={handlePanelLayout} 
          changeLayout={handlePanelLayout} 
          setCollectionUrls={props.setCollectionUrls}
          setCollectionUrls={props.setCollectionUrls}
          currentStep={currentStep}
          currentPage={currentPage}
          setMaxFootprintsMatched={setMaxFootprintsMatched}
        />
        />
      </InPortal>
      </InPortal>
    </>
    </>
+0 −17
Original line number Original line Diff line number Diff line
@@ -2,17 +2,8 @@ var _maxNumberPages = 0;
var _currentPage = 1;
var _currentPage = 1;
var _numberMatched = 0;
var _numberMatched = 0;
var _numberReturned = 0;
var _numberReturned = 0;
var _features = [];
var _limitVal = 10;
var _limitVal = 10;


/**
 * @function getFeatures
 * @description Gets the value of the max number of pages possible
 */
export function getFeatures() {
  return _features;
}



/**
/**
 * @function setNumberMatched
 * @function setNumberMatched
@@ -77,14 +68,6 @@ export function setCurrentPage(page) {
  _currentPage = page;
  _currentPage = page;
}
}


/**
 * @function getCurrentPage
 * @description Gets the value of the current page
 */
export function getCurrentPage() {
  return _currentPage;
}

/**
/**
 * @function setLimit
 * @function setLimit
 * @description Sets the value of the limit
 * @description Sets the value of the limit
+0 −1
Original line number Original line Diff line number Diff line
import L from "leaflet";
import L from "leaflet";
import "leaflet-draw";
import "leaflet-draw";
import Wkt from "wicket";
import Wkt from "wicket";
import {getCurrentPage} from "./ApiJsonCollection";


/**
/**
 * @class AstroDrawFilterControl
 * @class AstroDrawFilterControl
Loading