I have a problem with my application. On my page there are several components, Header.jsx, Hero.jsx, Footer etc. In Header I have implemented the search bar and it displays dynamic response, when I type it filters the results.
In the Hero component I have like a carousel of pictures. Under the carousel, there are 3 buttons (Step1, Step2, Step3). When I click on step1 it gets me the first picture, step2 the second etc.
The task I am given should make search options clickable and when I type for example "Software" in search prompt and when clicked it should get me the step1, or step2, or step3 doesn't matter. I will have 3 keywords when clicked it will be pointed to those steps, and also I will have the case for other keywords that will get me to the specific word or else on the page.
store.ts :
"use client";
// redux/store.ts
import { configureStore } from "@reduxjs/toolkit";
import ugoReducer from "./slices/ugoSlice";
import { type } from "os";
export const store = configureStore({
reducer: {
ugo: ugoReducer,
},
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
provider.ts:
"use client";
import { Provider } from "react-redux";
import { store } from "./store";
export function Providers({ children }) {
return <Provider store={store}>{
children}`your text`
</Provider>;
}
ugoSlice.ts:
"use client";
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
activeStepIndex: 0,
activeContent: null,
};
const ugoSlice = createSlice({
name: "ugo",
initialState,
reducers: {
setActiveStepIndex: (state, action) => {
state.activeStepIndex = action.payload;
},
setActiveContent: (state, action) => {
state.activeContent = action.payload;
},
},
});
export const { setActiveStepIndex, setActiveContent } = ugoSlice.actions;
export default ugoSlice.reducer;`
layout.tsx:
`
import "./globals.css";
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import { Providers } from "@/redux/provider";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
title: "pagepage",
description: "pagename",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className={inter.className}>
<Providers>{children}</Providers>
</body>
</html>
);
}
Header.jsx:
"use client";
import { Fragment, useEffect, useState, useRef } from "react";
import { usePathname } from "next/navigation";
import clsx from "clsx";
import { Menu, Popover, Transition } from "@headlessui/react";
import { MagnifyingGlassIcon } from "@heroicons/react/20/solid";
import { Bars3Icon, XMarkIcon } from "@heroicons/react/24/outline";
import { Logo } from "@/components/Logo";
import { useDispatch } from "react-redux";
import { setActiveStepIndex, setActiveContent } from "@/redux/slices/ugoSlice";
export function Header(props) {
const pathname = usePathname();
const { handleSearch } = props;
const [searchQuery, setSearchQuery] = useState("");
const [searchResults, setSearchResults] = useState([]);
const [showSearchResults, setShowSearchResults] = useState(false);
const searchContainerRef = useRef(null);
const dispatch = useDispatch(); // Create a dispatch function for Redux actions
const updateSuggestedOptions = (userInput) => {
const filteredResults = allSearchResults.filter((result) => {
const lowercaseResult = result.toLowerCase();
const lowercaseInput = userInput.toLowerCase();
return lowercaseResult.includes(lowercaseInput);
});
setSearchResults(filteredResults);
setShowSearchResults(true);
const selectedResult = filteredResults[0];
const activeStepIndex = UGO_CONTENT.findIndex((content) =>
content.key.includes(selectedResult)
);
dispatch(setActiveStepIndex(activeStepIndex));
dispatch(setActiveContent(selectedResult));
};
const allSearchResults = [
"Usluge",
"Galerija",
"Kontakt",
"Software",
"O nama",
"Ugostiteljstvo",
"Koristenje u lokalu",
];
useEffect(() => {
const script = document.createElement("script");
script.src = "//js-eu1.hs-scripts.com/143256357.js";
script.type = "text/javascript";
script.async = true;
script.defer = true;
document.body.appendChild(script);
return () => {
document.body.removeChild(script);
};
}, []);
const links = [
{ name: "Početna stranica", href: "/" },
{ name: "O nama", href: "/o-nama" },
// Add more navigation links as needed
];
const closeSearchOptions = (e) => {
if (
searchContainerRef.current &&
!searchContainerRef.current.contains(e.target)
) {
setShowSearchResults(false);
}
};
useEffect(() => {
document.addEventListener("click", closeSearchOptions);
return () => {
document.removeEventListener("click", closeSearchOptions);
};
}, []);
return (
<>
<Popover
as="header"
className={({ open }) =>
clsx(
{ "fixed inset-0 z-40 overflow-y-auto": open },
"bg-white shadow-lg lg:static lg:overflow-y-visible"
)
}
>
{({ open }) => (
<>
<div className="mx-auto max-w-large px-4 sm:px-6 lg:px-8">
<div className="relative flex justify-between lg:gap-8 xl:grid xl:grid-cols-12">
<div className="flex md:absolute md:inset-y-0 md:left-0 lg:static xl:col-span-1">
<div className="flex flex-shrink-0 items-center">
<a href="/">
<Logo className="mx-auto h-10 w-auto" />
</a>
</div>
</div>
{handleSearch && (
<div className="min-w-0 flex-1 md:px-8 lg:px-0 xl:col-span-7">
<div className="flex items-center px-6 py-4 md:mx-auto md:max-w-3xl lg:mx-0 lg:max-w-none xl:px-0">
<div className="w-full">
<label htmlFor="pretraga" className="sr-only">
Pretraga
</label>
<div className="relative">
<div className="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<MagnifyingGlassIcon
className="h-5 w-5 text-gray-400"
aria-hidden="true"
/>
</div>
<input
id="pretraga"
name="pretraga"
className="block w-full rounded-md border-0 bg-white py-1.5 pl-10 pr-3 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-500 sm:text-sm sm:leading-6"
placeholder="Pretraga"
type="search"
value={searchQuery}
onChange={(e) => {
setSearchQuery(e.target.value);
updateSuggestedOptions(e.target.value);
}}
/>
<button
onClick={/*handleSearch*/ updateSuggestedOptions}
className="absolute inset-y-0 right-0 px-3 py-1.5 text-white bg-primary rounded-md hover:bg-primary-dark focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
Pretraži
</button>
</div>
</div>
</div>
</div>
)}
<div className="flex items-center md:absolute md:inset-y-0 md:right-0 lg:hidden">
<Popover.Button className="relative -mx-2 inline-flex items-center justify-center rounded-md p-2 text-gray-400 hover:bg-gray-100 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500">
<span className="absolute -inset-0.5" />
<span className="sr-only">Open menu</span>
{open ? (
<XMarkIcon className="block h-6 w-6" aria-hidden="true" />
) : (
<Bars3Icon className="block h-6 w-6" aria-hidden="true" />
)}
</Popover.Button>
</div>
<div className="hidden lg:flex lg:items-center lg:justify-end xl:col-span-4">
<a
href="/"
className={clsx(
{ "bg-primary text-white": pathname === "/" },
{ "text-primary": pathname !== "/" },
"ml-6 inline-flex items-center rounded-lg pt-2 pb-2 pr-4 pl-4 text-sm font-semibold"
)}
>
Početna
</a>
<a
href="/o-nama"
className={clsx(
{ "bg-primary text-white": pathname === "/o-nama" },
{
"text-primary hover:text-secondary":
pathname !== "/o-nama",
},
"ml-6 inline-flex items-center rounded-lg pt-2 pb-2 pr-4 pl-4 text-sm text-primary font-semibold"
)}
>
O nama
</a>
<Menu as="div" className="relative ml-5 flex-shrink-0">
<div>
<Menu.Button className="relative flex rounded-full bg-white focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2">
<span className="absolute -inset-1.5" />
<span className="sr-only">Open user menu</span>
{/* <img className="h-8 w-8 rounded-full" src={user.imageUrl} alt="" /> */}
</Menu.Button>
</div>
<Transition
as={Fragment}
enter="transition ease-out duration-100"
enterFrom="transform opacity-0 scale-95"
enterTo="transform opacity-100 scale-100"
leave="transition ease-in duration-75"
leaveFrom="transform opacity-100 scale-100"
leaveTo="transform opacity-0 scale-95"
>
<Menu.Items className="absolute right-0 z-10 mt-2 w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
{[].map((item) => (
<Menu.Item key={item.name}>
{({ active }) => (
<a
href={item.href}
className={clsx(
{ "bg-gray-100": active },
"block px-4 py-2 text-sm text-gray-700"
)}
>
{item.name}
</a>
)}
</Menu.Item>
))}
</Menu.Items>
</Transition>
</Menu>
</div>
</div>
</div>
{/* Search results */}
{showSearchResults && (
<div
ref={searchContainerRef}
className="absolute top-20 left-1/2 transform -translate-x-1/2 bg-white shadow-lg p-4 mt-2 rounded-md w-3/4 sm:w-2/3 md:w-2/3"
>
<h2 className="text-lg font-semibold">Rezultati pretrage:</h2>
<ul>
{searchResults.map((result, index) => (
<li key={index} className="mt-2">
{result}
</li>
))}
</ul>
</div>
)}
</>
)}
</Popover>
</>
);
}
Hero.jsx :
"use client";
import React, { useState } from "react";
import clsx from "clsx";
import { Container } from "@/components/Container";
import RightArrowNav from "@/images/icons/right-arrow-nav.svg";
import LeftArrowNav from "@/images/icons/left-arrow-nav.svg";
import ContactForm from "./ContactForm";
import { useSelector, useDispatch } from "react-redux";
import { setActiveStepIndex } from "@/redux/slices/ugoSlice";
const modules = [
{
name: "UGO - ugostiteljstvo",
},
];
const UGO_CONTENT = [
{
key: "step-1",
image: "/images/download.jpeg",
title: "Step 1",
},
{
key: "step-2",
image: "/images/download1.jpeg",
title: "Step 2",
},
{
key: "step-3",
image: "/images/bg1.jpg",
title: "Step 3",
},
];
export function Hero() {
const [showContactForm, setShowContactForm] = useState(false);
const activeItemIndex = useSelector((state) => state.ugo.activeStepIndex);
const activeContent = useSelector((state) => state.ugo.activeContent);
const setActiveItemIndex = (index) => {
// Dispatch the action to update the active step index
dispatch(setActiveStepIndex(index));
};
return (
<Container className="pb-0 lg:pb-12 pt-0 lg:pt-12 lg:pt-24">
<div className="flex flex-col lg:flex-row justify-between h-full">
<div className="flex flex-row justify-center lg:flex-col pt-8 pb-8 gap-y-3">
{modules.map((module, index) => (
<button
key={index}
className={clsx(
{ "bg-primary text-white": activeItemIndex === index },
{ "text-primary": activeItemIndex !== index },
"rounded-lg pt-2 pb-2 font-semibold w-48 text-center text-sm rounded-md border-2 border-primary hover:bg-primary hover:text-white"
)}
onClick={() => setActiveItemIndex(index)}
>
{module.name}
</button>
))}
</div>
<div className="flex items-center w-full justify-between lg:pl-6 lg:pr-6">
<div className="hidden items-center lg:flex">
<LeftArrowNav
className={clsx(
{ "fill-gray-500 stroke-gray-400": activeItemIndex === 0 },
{ "cursor-pointer": activeItemIndex !== 0 },
"h-16"
)}
onClick={() => {
if (activeItemIndex !== 0) {
setActiveItemIndex(activeItemIndex - 1);
}
}}
/>
</div>
<div className="flex flex-col h-[80vh] lg:h-full w-full lg:ml-4 lg:mr-4 gap-y-4">
<div className="w-full h-full border rounded-md">
<div
className="w-full h-full"
style={{
backgroundImage: `url(${UGO_CONTENT[activeItemIndex].image})`,
}}
/>
</div>
<div className="flex justify-center gap-x-2">
<LeftArrowNav
className={clsx(
{ "fill-gray-500 stroke-gray-400": activeItemIndex === 0 },
{ "cursor-pointer": activeItemIndex !== 0 },
"w-3 h-8"
)}
onClick={() => {
if (activeItemIndex !== 0) {
setActiveItemIndex(activeItemIndex - 1);
}
}}
/>
{UGO_CONTENT.map((content, index) => (
<button
key={content.key}
className={clsx(
{ "bg-primary text-white": activeItemIndex === index },
{ "text-primary": activeItemIndex !== index },
"rounded-lg pt-1 pb-1 pl-2 pr-2 text-center text-sm rounded-md border-2 border-primary hover:bg-primary hover:text-white"
)}
onClick={() => setActiveItemIndex(index)}
>
{content.title}
</button>
))}
<RightArrowNav
className={clsx(
{
"fill-gray-500 stroke-gray-400":
activeItemIndex === UGO_CONTENT.length - 1,
},
{
"cursor-pointer":
activeItemIndex !== UGO_CONTENT.length - 1,
},
"w-3 h-8"
)}
onClick={() => {
if (activeItemIndex !== UGO_CONTENT.length - 1) {
setActiveItemIndex(activeItemIndex + 1);
}
}}
/>
</div>
</div>
<div className="hidden items-center lg:flex">
<RightArrowNav
className={clsx(
{
"fill-gray-500 stroke-gray-400":
activeItemIndex === UGO_CONTENT.length - 1,
},
{
"cursor-pointer": activeItemIndex !== UGO_CONTENT.length - 1,
},
"h-16"
)}
onClick={() => {
if (activeItemIndex !== UGO_CONTENT.length - 1) {
setActiveItemIndex(activeItemIndex + 1);
}
}}
/>
</div>
</div>
<div className="flex flex-col pt-8 pb-20 justify-between gap-y-3">
<div className="flex flex-col gap-y-3">
<button
className="flex justify-center items-center gap-2 group font-semibold rounded-lg pt-2 pb-2 w-full lg:w-48 text-center text-sm rounded-md border-2 border-primary bg-primary text-white"
onClick={() => setShowContactForm(!showContactForm)}
>
Kontakt forma{" "}
<RightArrowNav
className={clsx(
{ "rotate-90": showContactForm },
"h-3 cursor-pointer fill-white group-hover:fill-red"
)}
/>
</button>
{showContactForm && (
<ContactForm
showContactForm={showContactForm}
setShowContactForm={setShowContactForm}
/>
)}
<a href="mailto:[email protected]">
<button className="rounded-lg pt-2 pb-2 font-semibold w-full lg:w-48 text-center text-sm rounded-md border-2 border-primary bg-primary text-white">
Kontakt mailom
</button>
</a>
</div>
<div className="p-3 bg-primary text-white rounded-lg text-xs flex flex-col justify-center text-center ">
<div>Email: [email protected]</div>
<div>Tel: (+387) 33 xxx xxx</div>
<div>Lokacija: Grad</div>
</div>
</div>
</div>
</Container>
);
}