Skip to content

Commit

Permalink
Merge pull request fortran-lang#16 from arteevraina/global-search-redux
Browse files Browse the repository at this point in the history
feat: Global Search and Pagination
  • Loading branch information
henilp105 authored Feb 21, 2023
2 parents 2136747 + c80f3ae commit 0488ad5
Show file tree
Hide file tree
Showing 11 changed files with 338 additions and 74 deletions.
28 changes: 19 additions & 9 deletions flask/packages.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from flasgger.utils import swag_from
from urllib.parse import unquote
import json
import math

parameters = {
"name": "name",
Expand All @@ -33,11 +34,10 @@ def search_packages():
else "name"
)
page = int(page) if page else 0

query = unquote(query.strip().lower())
packages = (
db.packages.find(
{
packages_per_page = 10

mongo_db_query = {
"$and": [
{
"$or": [
Expand All @@ -48,7 +48,12 @@ def search_packages():
},
{"isDeprecated": False},
]
},
}


packages = (
db.packages.find(
mongo_db_query,
{
"_id": 0,
"name": 1,
Expand All @@ -58,20 +63,25 @@ def search_packages():
"tags": 1,
},
)
.sort(sorted_by, sort)
.limit(10)
.skip(page * 10)
.sort(sorted_by, -1)
.limit(packages_per_page)
.skip(page * packages_per_page)
)

if packages:
# Count the number of documents in the database related to query.
total_documents = db.packages.count_documents(mongo_db_query)

total_pages = math.ceil(total_documents / packages_per_page)

search_packages = []
for i in packages:
namespace = db.namespaces.find_one({"_id": i["namespace"]})
author = db.users.find_one({"_id": i["author"]})
i["namespace"] = namespace["namespace"]
i["author"] = author["name"]
search_packages.append(i)
return jsonify({"status": 200, "packages": search_packages}), 200
return jsonify({"status": 200, "packages": search_packages, "total_pages": total_pages}), 200
else:
return jsonify({"status": "error", "message": "packages not found"}), 404

Expand Down
16 changes: 15 additions & 1 deletion registry/src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,18 @@ body {

#card-text {
text-align: justify;
}
}

#list-item {
width: 80%;
}

#list-item-package-name {
color: #3382e5;
}

#dropdown-orderby {
display: flex;
align-items: center;
}

2 changes: 1 addition & 1 deletion registry/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import UserPage from "./pages/user";
// import PackagePage from "./pages/package";
import { BrowserRouter, Routes, Route } from "react-router-dom";

import 'bootstrap/dist/css/bootstrap.css';
import "bootstrap/dist/css/bootstrap.css";

function App() {
return (
Expand Down
39 changes: 39 additions & 0 deletions registry/src/components/packageItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { MDBListGroupItem } from "mdb-react-ui-kit";
import { Row, Col, Image } from "react-bootstrap";

const PackageItem = ({ packageEntity }) => {
return (
<MDBListGroupItem id="list-item">
<Row>
<Col md={1}>
<Image
src="https://fortran-lang.org/en/_static/fortran-logo-256x256.png"
fluid
width={60}
height={60}
/>
</Col>
<Col md={4}>
<div>
<h5 id="list-item-package-name">{packageEntity.name}</h5>
</div>
<h6 className="mb-2 text-muted">{packageEntity.description}</h6>
<h6 className="mb-2 text-muted">
Namespace {packageEntity.namespace}
</h6>
</Col>
<Col md={1} style={{ flex: 1 }}>
<h6
style={{
textAlign: "right",
}}
>
By {packageEntity.author}
</h6>
</Col>
</Row>
</MDBListGroupItem>
);
};

export default PackageItem;
54 changes: 54 additions & 0 deletions registry/src/components/pagination.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { useDispatch, useSelector } from "react-redux";
import {
MDBPagination,
MDBPaginationItem,
MDBPaginationLink,
} from "mdb-react-ui-kit";
import { searchPackage } from "../store/actions/searchActions";

const Pagination = ({ currentPage, totalPages }) => {
const query = useSelector((state) => state.search.query);
const orderBy = useSelector((state) => state.search.orderBy);

const maxVisibleItems = 5;
let startPage = Math.max(currentPage - Math.floor(maxVisibleItems / 2), 1);
let endPage = Math.min(startPage + maxVisibleItems - 1, totalPages);
const dispatch = useDispatch();

const handlePageChange = (page) => {
dispatch(searchPackage(query, page, orderBy));
};

return (
<nav aria-label="Pagination">
<MDBPagination className="mb-0">
<MDBPaginationItem
disabled={currentPage + 1 === 1}
onClick={() => handlePageChange(currentPage - 1)}
>
<MDBPaginationLink aria-disabled="true">Previous</MDBPaginationLink>
</MDBPaginationItem>
{Array.from(
{ length: endPage - startPage + 1 },
(_, i) => i + startPage
).map((page) => (
<MDBPaginationItem
key={page}
active={currentPage + 1 === page}
onClick={() => handlePageChange(page - 1)}
>
<MDBPaginationLink>{page}</MDBPaginationLink>
</MDBPaginationItem>
))}
<MDBPaginationItem
disabled={currentPage + 1 === totalPages}
onClick={() => handlePageChange(currentPage + 1)}
>
<MDBPaginationLink>Next</MDBPaginationLink>
</MDBPaginationItem>
</MDBPagination>
</nav>
);
};

export default Pagination;
12 changes: 7 additions & 5 deletions registry/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,20 @@ const persistConfig = {
key: "root",
storage,
transforms: [authTransform],
whitelist: ["auth"],
};

const persistedReducer = persistReducer(persistConfig, rootReducer);

const root = ReactDOM.createRoot(document.getElementById("root"));
const store = configureStore({
reducer: persistedReducer,
middleware: (getDefaultMiddleware) => getDefaultMiddleware({
serializableCheck: {
ignoreActions: [REGISTER]
}
}),
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
ignoreActions: [REGISTER],
},
}),
});

const persistor = persistStore(store);
Expand Down
22 changes: 19 additions & 3 deletions registry/src/pages/Navbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
import { useDispatch, useSelector } from "react-redux";
import Image from "react-bootstrap/Image";
import { logout } from "../store/actions/authActions";
import { searchPackage, setQuery } from "../store/actions/searchActions";

const Navbar = () => {
const isAuthenticated = useSelector((state) => state.auth.isAuthenticated);
Expand Down Expand Up @@ -117,15 +118,30 @@ const Navbar = () => {

export default Navbar;

function SearchBar() {
const SearchBar = () => {
const query = useSelector((state) => state.search.query);
const navigate = useNavigate();
const dispatch = useDispatch();

const search = () => {
if (query.trim().length !== 0) {
dispatch(searchPackage(query, 0));
navigate("/search");
}
};

return (
<div className="d-flex">
<input
type="text"
className="flex-fill form-control"
placeholder="Search"
value={query}
onChange={(event) => dispatch(setQuery(event.target.value))}
/>
<button className="btn btn-primary">Search</button>
<button className="btn btn-primary" onClick={search}>
Search
</button>
</div>
);
}
};
115 changes: 60 additions & 55 deletions registry/src/pages/search.js
Original file line number Diff line number Diff line change
@@ -1,64 +1,69 @@
import React, { useState } from "react";
// import "./search.css";
import React from "react";
import { useSelector, useDispatch } from "react-redux";
import PackageItem from "../components/packageItem";
import { MDBListGroup } from "mdbreact";
import Pagination from "../components/pagination";
import Dropdown from "react-bootstrap/Dropdown";
import DropdownButton from "react-bootstrap/DropdownButton";
import { searchPackage, setOrderBy } from "../store/actions/searchActions";

const Search = () => {
const [query, setQuery] = useState("");
const [results, setResults] = useState([]);
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
const packages = useSelector((state) => state.search.packages);
const error = useSelector((state) => state.search.error);
const totalPages = useSelector((state) => state.search.totalPages);
const currentPage = useSelector((state) => state.search.currentPage);
const orderBy = useSelector((state) => state.search.orderBy);
const query = useSelector((state) => state.search.query);

const handleSubmit = (event) => {
event.preventDefault();
const url = `${
process.env.REACT_APP_REGISTRY_API_URL
}/packages?query=${encodeURIComponent(query)}`;
fetch(url)
.then((response) => response.json())
.then((data) => {
setResults(data["packages"]);
});
};
const dropdownOptions = ["None", "Date last updated"];

const loadQuery = () => {
console.log("urlParams.get(query)");
setQuery(urlParams.get("query"));
const url = `${
process.env.REACT_APP_REGISTRY_API_URL
}/packages?query=${encodeURIComponent(query)}`;
fetch(url)
.then((response) => response.json())
.then((data) => {
setResults(data["packages"]);
});
};
const dispatch = useDispatch();

return (
<div class="container" onLoad={loadQuery}>
fpm-registry Package Search
<form onSubmit={handleSubmit}>
<input
type="text"
id="query"
name="query"
placeholder="Enter your search query"
value={query}
onChange={(event) => setQuery(event.target.value)}
/>
<button type="submit">Search</button>
</form>
<div id="result">
{results.map((result) => (
<div>
<h2>{result.name}</h2>
<p>{result.description}</p>
<p>{result.author}</p>
<p>{result.description}</p>
<p>{result.namespace}</p>
</div>
))}
</div>
return error !== null ? (
<div className="container">
<div id="result">{error}</div>
</div>
);
) : packages !== null ? (
packages.length === 0 ? (
<div className="container">
<div>No packages found.</div>
</div>
) : (
<div className="container">
<br />
<div className="d-flex justify-content-end" id="dropdown-orderby">
<label>Order by</label>
<DropdownButton title={orderBy}>
{dropdownOptions.map((option) => (
<Dropdown.Item
key={option}
onClick={() => {
dispatch(setOrderBy(option));
dispatch(searchPackage(query, 0, option));
}}
>
{option}
</Dropdown.Item>
))}
</DropdownButton>
</div>
<MDBListGroup
style={{
alignItems: "center",
}}
>
{packages.map((packageEntity) => (
<PackageItem
key={packageEntity.name + packageEntity.namespace}
packageEntity={packageEntity}
/>
))}
<br />
<Pagination currentPage={currentPage} totalPages={totalPages} />
</MDBListGroup>
</div>
)
) : null;
};

export default Search;
Loading

0 comments on commit 0488ad5

Please sign in to comment.