Skip to content

Commit

Permalink
feat(frontend): ✨ Input search product
Browse files Browse the repository at this point in the history
  • Loading branch information
kjarret committed Jul 17, 2024
1 parent 2fbadc4 commit 5eb9ae6
Show file tree
Hide file tree
Showing 7 changed files with 187 additions and 77 deletions.
119 changes: 46 additions & 73 deletions frontend/src/components/headers/MainHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import { useCart } from "contexts/CartContext";
import Image from "next/image";
import Link from "next/link";
import { useState } from "react";
import { FaShoppingBag, FaTimes, FaUserCircle } from "react-icons/fa";
import { FaShoppingBag, FaUserCircle } from "react-icons/fa";

import { IoMdSearch } from "react-icons/io";
import { RiListSettingsLine } from "react-icons/ri";

import DropdownMenu from "components/ui/DropdownMenu";
import styles from "../../styles/components/MainHeader.module.scss";
import DropdownMenuProfile from "components/ui/DropdownMenuProfile";
import SearchInput from "components/ui/SearchInput";
import styles from "../../styles/components/MainHeader.module.scss";

export default function MainHeader() {
const [searchActive, setSearchActive] = useState(false);
Expand Down Expand Up @@ -53,60 +53,29 @@ export default function MainHeader() {
</div>
<div className="relative flex items-center">
<div className="flex items-center">
<input
type="text"
placeholder={searchActive ? "Rechercher..." : ""}
value={searchActive ? searchValue : ""}
onChange={handleInputChange}
readOnly={!searchActive}
className={`${
searchActive ? "w-60 pl-8 pr-7" : "w-9"
} overflow-hidden rounded-full border text-black transition-all duration-300 focus:border-blue-500 focus:outline-none ${
styles.searchInput
} ${!searchActive && "cursor-pointer"}`}
style={{
borderRadius: "10px",
borderColor: "#5461fc",
borderWidth: "2px",
}}
/>
<div
className="absolute inset-y-0 left-0 flex items-center pl-2.5"
onClick={toggleSearch}
>
<IoMdSearch className="cursor-pointer stroke-2 text-black" />
</div>
{searchActive && (
<div
className="absolute inset-y-0 right-0 flex items-center pr-3 text-gray-400"
onClick={() => {
setSearchActive(false);
setSearchValue("");
}}
>
<FaTimes className="cursor-pointer text-black" />
</div>
)}
<SearchInput />
</div>
</div>
<div className={styles.rightLinks}>
<Link href="/products" className={styles.allArticles}>
<span>Tous les articles</span>
</Link>

{localStorage.getItem("jwt") ?
<div
className="relative"
onMouseEnter={() => setMenuVisible(true)}
onMouseLeave={() => setMenuVisible(false)}
>
<RiListSettingsLine
className="cursor-pointer text-white ease-out hover:text-indigo-500"
size={32}
/>
{menuVisible && <DropdownMenu />}
</div>
: ""}

{localStorage.getItem("jwt") ? (
<div
className="relative"
onMouseEnter={() => setMenuVisible(true)}
onMouseLeave={() => setMenuVisible(false)}
>
<RiListSettingsLine
className="cursor-pointer text-white ease-out hover:text-indigo-500"
size={32}
/>
{menuVisible && <DropdownMenu />}
</div>
) : (
""
)}

<Link href="/cart">
<div className="relative ease-out hover:scale-90 hover:text-indigo-500">
Expand All @@ -116,28 +85,32 @@ export default function MainHeader() {
</div>
</div>
</Link>
{localStorage.getItem("jwt") ?

<div
className="relative"
onMouseEnter={() => setMenuProfileVisible(true)}
onMouseLeave={() => setMenuProfileVisible(false)}
>
<FaUserCircle
className="text-white ease-out hover:scale-90 hover:text-indigo-500"
size={32}
/>
{menuProfileVisible && <DropdownMenuProfile setMenuProfileVisible={ setMenuProfileVisible}/>}
</div> :
<Link href="/login">
<div>
<FaUserCircle
className="text-white ease-out hover:scale-90 hover:text-indigo-500"
size={32}
/>
</div>
</Link>
}
{localStorage.getItem("jwt") ? (
<div
className="relative"
onMouseEnter={() => setMenuProfileVisible(true)}
onMouseLeave={() => setMenuProfileVisible(false)}
>
<FaUserCircle
className="text-white ease-out hover:scale-90 hover:text-indigo-500"
size={32}
/>
{menuProfileVisible && (
<DropdownMenuProfile
setMenuProfileVisible={setMenuProfileVisible}
/>
)}
</div>
) : (
<Link href="/login">
<div>
<FaUserCircle
className="text-white ease-out hover:scale-90 hover:text-indigo-500"
size={32}
/>
</div>
</Link>
)}
</div>
</header>
</main>
Expand Down
112 changes: 112 additions & 0 deletions frontend/src/components/ui/SearchInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { useQuery } from "@apollo/client";
import { GET_ALL_PRODUCTS_BY_KEYWORD } from "lib/graphql/queries";
import Link from "next/link";
import { useEffect, useState } from "react";
import { FaTimes } from "react-icons/fa";
import { IoMdSearch } from "react-icons/io";

const SearchInput = () => {
const [searchActive, setSearchActive] = useState(false);
const [searchValue, setSearchValue] = useState("");

const { data, loading, error, refetch } = useQuery(
GET_ALL_PRODUCTS_BY_KEYWORD,
{
variables: { keyword: searchValue },
skip: !searchActive || !searchValue,
},
);

const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setSearchValue(event.target.value);
if (event.target.value) {
refetch();
}
};

const toggleSearch = () => {
setSearchActive((prev) => !prev);
if (!searchActive) {
setSearchValue("");
}
};

useEffect(() => {
if (!searchActive) {
setSearchValue("");
}
}, [searchActive]);

const productsByKeyword = data?.getAllProductsByKeyword || [];

return (
<div className="relative flex items-center">
<input
type="text"
placeholder={searchActive ? "Rechercher..." : ""}
value={searchActive ? searchValue : ""}
onChange={handleInputChange}
readOnly={!searchActive}
className={`border text-black transition-all duration-300 focus:border-blue-500 focus:outline-none ${
searchActive ? "w-60 pl-8 pr-7" : "w-9 cursor-pointer"
} overflow-hidden rounded-full`}
style={{
borderRadius: "10px",
borderColor: "#5461fc",
borderWidth: "2px",
}}
/>
<div
className="absolute inset-y-0 left-0 flex items-center pl-2.5"
onClick={toggleSearch}
>
<IoMdSearch className="cursor-pointer stroke-2 text-black" />
</div>
{searchActive && (
<div
className="absolute inset-y-0 right-0 flex items-center pr-3 text-gray-400"
onClick={() => {
setSearchActive(false);
setSearchValue("");
}}
>
<FaTimes className="cursor-pointer text-black" />
</div>
)}
{searchActive && searchValue && (
<div className="absolute top-full z-50 mt-2 w-60 rounded-md border border-gray-300 bg-white shadow-lg">
{loading ? (
<div className="p-2">Loading...</div>
) : error ? (
<div className="p-2 text-red-500">Error: {error.message}</div>
) : productsByKeyword.length > 0 ? (
<ul>
{productsByKeyword.map((product: any) => (
<Link
href={`/products/${product.id}`}
key={product.id}
onClick={toggleSearch}
>
<li className="flex cursor-pointer items-center justify-start p-2 text-black hover:bg-gray-100">
<img
src={product.picture[0]}
alt={"Image de " + product.name}
className="mr-6 h-12 w-12 rounded-md"
/>
{product.name}
</li>
</Link>
))}
</ul>
) : (
<div className="text-black-500 bg-red-500 p-2">
No results found
</div>
)}
</div>
)}
</div>
);
};

export default SearchInput;
4 changes: 2 additions & 2 deletions frontend/src/containers/public/home/summer-products.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ const HomeHotProductsSection = () => {

const filteredProducts = products.filter(
(product: any) =>
product.category.name === "Escalade" ||
product.category.name === "Camping",
product.category?.name === "Escalade" ||
product.category?.name === "Camping",
);

return (
Expand Down
22 changes: 22 additions & 0 deletions frontend/src/lib/graphql/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,3 +108,25 @@ export const GET_USER_PROFILE = gql`
}
}
`;

export const GET_ALL_PRODUCTS_BY_KEYWORD = gql`
query GetAllProductsByKeyword($keyword: String!) {
getAllProductsByKeyword(keyword: $keyword) {
id
name
description_short
description_long
picture
price_fixed
price_daily
discount
quantity
created_at
updated_at
category {
name
id
}
}
}
`;
1 change: 0 additions & 1 deletion frontend/src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ function App({ Component, pageProps: { ...pageProps } }: AppProps) {
<UserDatesResearchProvider>
<Component {...pageProps} />
</UserDatesResearchProvider>

<ToastContainer />
</Layout>
</CartProvider>
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/pages/login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ const LoginPage = () => {
const router = useRouter();
const [errorMessage, setErrorMessage] = useState("");

if (localStorage.getItem("jwt") !== null) {
router.push("/profile");
}

const togglePasswordVisibility = () => {
setShowPassword(!showPassword);
};
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/pages/profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const ProfilePage = () => {

useEffect(() => {
refetch();
}, []);
});

useEffect(() => {
if (error && error.message.includes("not authenticated")) {
Expand Down

0 comments on commit 5eb9ae6

Please sign in to comment.