🏡 index : ~doyle/chartered.git

author Jordan Doyle <jordan@doyle.la> 2021-10-09 12:26:53.0 +01:00:00
committer Jordan Doyle <jordan@doyle.la> 2021-10-09 12:26:53.0 +01:00:00
commit
9c905ceab9debebbbdbd3f83bc5c195497d08bcc [patch]
tree
3d0ae3f0375b3644be93923b8812c5cbd609cd5d
parent
1916f2b8ae1f7cdb3eadc351e2e7e199126d4922
download
9c905ceab9debebbbdbd3f83bc5c195497d08bcc.tar.gz

Implement search results for users



Diff

 chartered-frontend/src/index.sass       |  8 ++++++++
 chartered-frontend/src/index.tsx        |  6 ++++++
 chartered-frontend/src/util.tsx         |  3 ++-
 chartered-frontend/src/pages/Search.tsx | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 chartered-frontend/src/sections/Nav.tsx | 40 ++++++++++++++++++++++++++++++++++++++--
 5 files changed, 123 insertions(+), 9 deletions(-)

diff --git a/chartered-frontend/src/index.sass b/chartered-frontend/src/index.sass
index 879e263..1697bf7 100644
--- a/chartered-frontend/src/index.sass
+++ a/chartered-frontend/src/index.sass
@@ -42,3 +42,11 @@
    &::after
        content: ""
        border-top: 1px solid

.navbar .form-control
    transition: width 0.5s ease-in-out
    width: 250px

    &:focus, &:not(:placeholder-shown)
        width: 350px

diff --git a/chartered-frontend/src/index.tsx b/chartered-frontend/src/index.tsx
index 140e9e5..4635203 100644
--- a/chartered-frontend/src/index.tsx
+++ a/chartered-frontend/src/index.tsx
@@ -23,6 +23,7 @@
import OrganisationView from "./pages/crate/OrganisationView";
import CreateOrganisation from "./pages/organisations/CreateOrganisation";
import User from "./pages/User";
import Search from "./pages/Search";

function App() {
  return (
@@ -92,6 +93,11 @@
            exact
            path="/organisations/create"
            component={() => <CreateOrganisation />}
          />
          <PrivateRoute
            exact
            path="/search"
            component={() => <Search />}
          />
        </Switch>
      </Router>
diff --git a/chartered-frontend/src/util.tsx b/chartered-frontend/src/util.tsx
index 2de96bd..93dfab4 100644
--- a/chartered-frontend/src/util.tsx
+++ a/chartered-frontend/src/util.tsx
@@ -24,6 +24,7 @@

  React.useEffect(async () => {
    try {
      setResponse(null);
      let res = await fetch(authenticatedEndpoint(auth, endpoint));

      if (res.status == 401) {
@@ -94,7 +95,7 @@
  } else {
    return (
      <div
        className={`rounded-circle ${className}`}
        className={`rounded-circle d-inline-block ${className}`}
        style={{ width, height, background: "rgb(235, 235, 235)" }}
      />
    );
diff --git a/chartered-frontend/src/pages/Search.tsx b/chartered-frontend/src/pages/Search.tsx
new file mode 100644
index 0000000..f7048eb 100644
--- /dev/null
+++ a/chartered-frontend/src/pages/Search.tsx
@@ -1,0 +1,75 @@
import React = require("react");
import { useState, useEffect } from "react";
import { Link, useHistory, useLocation } from "react-router-dom";

import Nav from "../sections/Nav";
import { useAuth } from "../useAuth";
import { authenticatedEndpoint, ProfilePicture, useAuthenticatedRequest } from "../util";

import { Plus } from "react-bootstrap-icons";
import { LoadingSpinner } from "./Loading";

interface UsersSearchResponse {
    users: UserSearchResponseUser[];
}

interface UserSearchResponseUser {
    user_uuid: string;
    display_name: string;
    picture_url: string;
}

export default function Search() {
  const auth = useAuth();
  const location = useLocation();

  const query = location.pathname === '/search'
    ? new URLSearchParams(location.search).get("q") || ""
    : "";

  return (
    <div className="text-white">
      <Nav />

      <div className="container mt-4 pb-4">
        <h1>Search Results {query ? <>for '{query}'</> : <></>}</h1>

        <UsersResults query={query} />
      </div>
    </div>
  );
}

function UsersResults({ query }: { query: string }) {
    const auth = useAuth();

    const { response: results, error } =
        useAuthenticatedRequest<UsersSearchResponse>({
            auth,
            endpoint: "users/search?q=" + encodeURIComponent(query),
        }, [query]);

    if (!results) {
        return <div className="card border-0 shadow-sm text-black p-2">
            <div className="card-body">
                {[0, 1, 2].map((i) => (
                    <ProfilePicture key={i} height="5rem" width="5rem" className="me-2" src={undefined} />
                ))}
            </div>
        </div>
    }

    if (results?.users.length === 0) {
        return <></>;
    }

    return <div className="card border-0 shadow-sm text-black p-2">
        <div className="card-body">            
            {results.users.map((user, i) => (
                <Link to={`users/${user.user_uuid}`}>
                    <ProfilePicture key={i} height="5rem" width="5rem" className="me-2" src={user.picture_url} />
                </Link>
            ))}
        </div>
    </div>;
}
diff --git a/chartered-frontend/src/sections/Nav.tsx b/chartered-frontend/src/sections/Nav.tsx
index fd4782b..b795532 100644
--- a/chartered-frontend/src/sections/Nav.tsx
+++ a/chartered-frontend/src/sections/Nav.tsx
@@ -1,17 +1,33 @@
import React = require("react");
import { useHistory, useLocation } from "react-router-dom";
import { NavLink, Link } from "react-router-dom";

import { BoxArrowRight } from "react-bootstrap-icons";
import { BoxArrowRight, Search } from "react-bootstrap-icons";
import { useAuth } from "../useAuth";

export default function Nav() {
  const auth = useAuth();
  const history = useHistory();
  const location = useLocation();

  const logout = async (e) => {
    e.preventDefault();
    await auth.logout();
  };

  const [search, setSearch] = React.useState(
    location.pathname === '/search'
      ? new URLSearchParams(location.search).get("q") || ""
      : ""
  );
  const submitSearchForm = (e) => {
    e.preventDefault();

    if (search != "") {
      history.push(`/search?q=${encodeURIComponent(search)}`)
    }
  };

  return (
    <nav className="navbar navbar-expand-lg navbar-light bg-white shadow-sm">
      <div className="container-fluid">
@@ -48,14 +64,22 @@
              </NavLink>
            </li>
          </ul>

          <form className="d-flex" onSubmit={submitSearchForm}>
            <div className="input-group">
              <span className="input-group-text bg-transparent border-none">
                <Search />
              </span>

          <form className="d-flex">
            <input
              className="form-control me-2"
              type="search"
              placeholder="Search"
              aria-label="Search"
            />
              <input
                className="form-control me-2"
                type="search"
                placeholder="Search"
                aria-label="Search"
                value={search}
                onChange={(e) => setSearch(e.target.value)}
              />
            </div>
          </form>

          <div>