From be42426e294c19118ef81f4c596476f5cebb70eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mathilde=20Hauk=C3=B8=20Haugum?= <69193134+mathildehaugum@users.noreply.github.com> Date: Wed, 11 Oct 2023 11:22:00 +0200 Subject: [PATCH] 116 search for consultant (#117) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Create searchbar and sidebar * use React-Feather for icons * Add min height to side bar * Implement filtering of variants based on input to search bar * Add focus to searchbar on keystrokes * remove gitignore for prettierrc * Try to fix lowercase filenames in components * Rename FilteredVariantsList * add func eslint rule * Fix linting error * Change to more general naming --------- Co-authored-by: Sigrid Elnan Co-authored-by: Jonas Bjøralt --- .gitignore | 1 - frontend/.eslintrc.json | 5 +- frontend/.prettierrc | 1 + frontend/package.json | 1 + frontend/public/icons/chevron-down.svg | 3 - frontend/src/app/bemanning/layout.tsx | 18 ++++++ frontend/src/app/bemanning/page.tsx | 22 +++++++ frontend/src/app/globals.css | 5 ++ frontend/src/app/layout.tsx | 4 +- frontend/src/app/page.tsx | 32 +--------- ...tElement.tsx => ConsultantListElement.tsx} | 23 +++---- .../components/FilteredConsultantsList.tsx | 41 +++++++++++++ frontend/src/components/PageLayout.tsx | 61 +++++++++---------- .../src/components/SearchBarComponent.tsx | 47 ++++++++++++++ frontend/tailwind.config.js | 1 + frontend/yarn.lock | 9 ++- 16 files changed, 190 insertions(+), 84 deletions(-) create mode 100644 frontend/.prettierrc delete mode 100644 frontend/public/icons/chevron-down.svg create mode 100644 frontend/src/app/bemanning/layout.tsx create mode 100644 frontend/src/app/bemanning/page.tsx rename frontend/src/components/{variantListElement.tsx => ConsultantListElement.tsx} (64%) create mode 100644 frontend/src/components/FilteredConsultantsList.tsx create mode 100644 frontend/src/components/SearchBarComponent.tsx diff --git a/.gitignore b/.gitignore index 50cf959f..65e7d5a1 100644 --- a/.gitignore +++ b/.gitignore @@ -455,4 +455,3 @@ $RECYCLE.BIN/ !.vscode/extensions.json *.[Ll]ocal.json -frontend/.prettierrc diff --git a/frontend/.eslintrc.json b/frontend/.eslintrc.json index dffe8401..61168913 100644 --- a/frontend/.eslintrc.json +++ b/frontend/.eslintrc.json @@ -1,3 +1,6 @@ { - "extends": ["next"] + "extends": ["next"], + "rules": { + "func-style": ["error", "declaration"] + } } diff --git a/frontend/.prettierrc b/frontend/.prettierrc new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/frontend/.prettierrc @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index c0ea8df0..2fb5f58a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -25,6 +25,7 @@ "postcss": "^8.4.31", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-feather": "^2.0.10", "react-query": "^3.39.3", "tailwindcss": "^3.3.3", "typescript": "5.2.2" diff --git a/frontend/public/icons/chevron-down.svg b/frontend/public/icons/chevron-down.svg deleted file mode 100644 index 8659044a..00000000 --- a/frontend/public/icons/chevron-down.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/frontend/src/app/bemanning/layout.tsx b/frontend/src/app/bemanning/layout.tsx new file mode 100644 index 00000000..c4f698ff --- /dev/null +++ b/frontend/src/app/bemanning/layout.tsx @@ -0,0 +1,18 @@ +import SearchBarComponent from "@/components/SearchBarComponent"; + +export default function BemanningLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( +
+
+

Filter

+ +
+ +
{children}
+
+ ); +} diff --git a/frontend/src/app/bemanning/page.tsx b/frontend/src/app/bemanning/page.tsx new file mode 100644 index 00000000..1ce68cda --- /dev/null +++ b/frontend/src/app/bemanning/page.tsx @@ -0,0 +1,22 @@ +"use client"; + +import FilteredConsultantsList from "@/components/FilteredConsultantsList"; +import useVibesApi from "@/hooks/useVibesApi"; +import { CircularProgress } from "@mui/material"; + +export default function Bemanning() { + const { data, isLoading } = useVibesApi(true); + + if (isLoading) { + return ; + } + + if (data) { + return ( +
+

Konsulenter

+ +
+ ); + } +} diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css index 0e344a40..d844075f 100644 --- a/frontend/src/app/globals.css +++ b/frontend/src/app/globals.css @@ -99,4 +99,9 @@ font-family: "Graphik-Medium"; line-height: 1.25rem; } + + .input { + flex: 1; + background-color: transparent; + } } diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index 0870ad17..551f0a96 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -17,9 +17,7 @@ export default function RootLayout({ /> - -
{children}
-
+ {children} ); diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index 766ca8a7..278b66c4 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -1,31 +1 @@ -"use client"; -import VariantListElement from "@/components/variantListElement"; -import useVibesApi from "@/hooks/useVibesApi"; -import { CircularProgress } from "@mui/material"; - -export default function Bemanning() { - const { data, isLoading } = useVibesApi(true); - - if (isLoading) { - return ; - } - - if (data) { - return ( -
-

Konsulenter

- -
-

Konsulentliste

- -
-

{data.length}

-
-
- {data.map((variant) => ( - - ))} -
- ); - } -} +export default function Root() {} diff --git a/frontend/src/components/variantListElement.tsx b/frontend/src/components/ConsultantListElement.tsx similarity index 64% rename from frontend/src/components/variantListElement.tsx rename to frontend/src/components/ConsultantListElement.tsx index 7fb9c251..7574c008 100644 --- a/frontend/src/components/variantListElement.tsx +++ b/frontend/src/components/ConsultantListElement.tsx @@ -1,17 +1,20 @@ "use client"; import { Variant } from "@/types"; import { useState } from "react"; +import { ChevronDown } from "react-feather"; -interface VariantListElementProps { - variant: Variant; +interface ConsultantListElementProps { + consultant: Variant; } -const VariantListElement = ({ variant }: VariantListElementProps) => { +export default function ConsultantListElement({ + consultant, +}: ConsultantListElementProps) { const [isListElementVisible, setIsListElementVisible] = useState(false); - const toggleListElementVisibility = () => { + function toggleListElementVisibility() { setIsListElementVisible(!isListElementVisible); - }; + } return (
{ onClick={toggleListElementVisibility} >
- chevron-down +
-

{variant.name}

-

{variant.email}

+

{consultant.name}

+

{consultant.email}

); -}; - -export default VariantListElement; +} diff --git a/frontend/src/components/FilteredConsultantsList.tsx b/frontend/src/components/FilteredConsultantsList.tsx new file mode 100644 index 00000000..7c04bcc9 --- /dev/null +++ b/frontend/src/components/FilteredConsultantsList.tsx @@ -0,0 +1,41 @@ +"use client"; +import { useEffect, useState } from "react"; +import ConsultantListElement from "./ConsultantListElement"; +import { Variant } from "@/types"; +import { useSearchParams } from "next/navigation"; + +export default function FilteredConsultantList({ + consultants, +}: { + consultants: Variant[]; +}) { + const searchParams = useSearchParams(); + const search = searchParams.get("search"); + const [filteredConsultants, setFilteredConsultants] = useState(consultants); + + useEffect(() => { + if (search && search.length > 0) { + const filtered = consultants?.filter((consultant) => + consultant.name.toLowerCase().includes(search.toLowerCase()), + ); + setFilteredConsultants(filtered); + } else { + setFilteredConsultants(consultants); + } + }, [search, consultants]); + + return ( +
+
+

Konsulentliste

+ +
+

{filteredConsultants?.length}

+
+
+ {filteredConsultants?.map((consultant) => ( + + ))} +
+ ); +} diff --git a/frontend/src/components/PageLayout.tsx b/frontend/src/components/PageLayout.tsx index d4641162..caef3e99 100644 --- a/frontend/src/components/PageLayout.tsx +++ b/frontend/src/components/PageLayout.tsx @@ -1,38 +1,33 @@ -"use client" -import { AuthenticatedTemplate, UnauthenticatedTemplate } from "@azure/msal-react"; +"use client"; +import { + AuthenticatedTemplate, + UnauthenticatedTemplate, +} from "@azure/msal-react"; import { Box, Container, Grid } from "@mui/material"; import VibesAppBar from "./VibesNavBar"; import SignInSignOutButton from "./vibes-buttons/SignInSignOutButton"; -export default function PageLayout({ children }: { children: React.ReactNode }) { - return ( - - - - - - {children} - - +export default function PageLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( +
+ + {children} - - - Please log in first - - - - - - ); -} \ No newline at end of file + + + Please log in first + + + +
+ ); +} diff --git a/frontend/src/components/SearchBarComponent.tsx b/frontend/src/components/SearchBarComponent.tsx new file mode 100644 index 00000000..ecfa1047 --- /dev/null +++ b/frontend/src/components/SearchBarComponent.tsx @@ -0,0 +1,47 @@ +"use client"; +import { useRouter } from "next/navigation"; +import { useEffect, useRef, useState } from "react"; +import { Search } from "react-feather"; + +export default function SearchBarComponent() { + const router = useRouter(); + const [searchText, setSearchText] = useState(""); + const inputRef = useRef(null); + + useEffect(() => { + router.push(`/bemanning?search=${searchText}`); + }, [searchText, router]); + + useEffect(() => { + function keyDownHandler(e: { code: string }) { + if ( + (e.code.startsWith("Key") || e.code.includes("Backspace")) && + inputRef.current + ) { + inputRef.current.focus(); + } + } + document.addEventListener("keydown", keyDownHandler); + + // clean up + return () => { + document.removeEventListener("keydown", keyDownHandler); + }; + }, []); + + return ( +
+

Søk

+
+ + setSearchText(e.target.value)} + autoFocus + ref={inputRef} + > +
+
+ ); +} diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index 14bebb49..13c3e9e1 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -6,6 +6,7 @@ module.exports = { black: "#333333", white: "#FFFFFF", primary_default: "#423D89", + primary_l1: "#A09EC4", primary_l3: "#ECECF3", primary_l4: "#F6F5F9", secondary_default: "#F076A6", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index ce567503..e517aaa4 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2461,7 +2461,7 @@ prettier@3.0.3: resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.0.3.tgz#432a51f7ba422d1469096c0fdc28e235db8f9643" integrity sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg== -prop-types@^15.6.2, prop-types@^15.8.1: +prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -2488,6 +2488,13 @@ react-dom@^18.2.0: loose-envify "^1.1.0" scheduler "^0.23.0" +react-feather@^2.0.10: + version "2.0.10" + resolved "https://registry.yarnpkg.com/react-feather/-/react-feather-2.0.10.tgz#0e9abf05a66754f7b7bb71757ac4da7fb6be3b68" + integrity sha512-BLhukwJ+Z92Nmdcs+EMw6dy1Z/VLiJTzEQACDUEnWMClhYnFykJCGWQx+NmwP/qQHGX/5CzQ+TGi8ofg2+HzVQ== + dependencies: + prop-types "^15.7.2" + react-is@^16.13.1, react-is@^16.7.0: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"