Added Navbar with nested entries

This commit is contained in:
Joao Ramos
2024-02-08 22:23:22 +00:00
parent 36d6c8c405
commit 2fd05915a7
3 changed files with 358 additions and 162 deletions

View File

@@ -1,176 +1,292 @@
/* eslint-disable @next/next/no-img-element */ /* eslint-disable @next/next/no-img-element */
"use client"; "use client";
import React from "react";
import Link from "next/link"; import Link from "next/link";
import Image from "next/image"; import Image from "next/image";
import clsx from "clsx"; import clsx from "clsx";
import {BsList, BsXLg} from "react-icons/bs"; import { BsList, BsXLg } from "react-icons/bs";
import {Fragment, useEffect, useState} from "react"; import { Fragment, useState } from "react";
import {Dialog, Menu, Transition} from "@headlessui/react"; import { Dialog, Transition } from "@headlessui/react";
import {useRouter} from "next/navigation";
import translation from "@/translation/navbar.json"; import translation from "@/translation/navbar.json";
import NestedNavbarEntry from "@/components/NestedNavbarEntry";
const items = [ const items = [
{page: "/", key: "home"}, { page: "/", key: "home" },
{page: "/services", key: "services"}, { page: "/services", key: "services" },
{page: "/price", key: "price"}, { page: "/price", key: "price" },
{page: "/about", key: "about"}, { page: "/about", key: "about" },
{page: "/history", key: "history"}, { page: "/history", key: "history" },
{page: "/contact", key: "contact"}, { page: "/contact", key: "contact" },
{
key: "country_manager",
page: "",
entries: [
{
key: "Egypt",
entries: [
{
name: "Modern Technology",
number: "+226578480830",
email: "egypt@encoach.com",
},
],
},
{
key: "Oman",
entries: [
{
name: "Smartway education",
number: "+9689944094",
email: "oman@encoach.com",
},
{ name: "MTI", number: "+9687445609", email: "mti@encoach.com" },
],
},
{
key: "Saudi Arabia",
entries: [
{
name: "Edutrach services",
number: "+96658499347",
email: "edu@encoach.com",
},
],
},
],
},
]; ];
export default function Navbar({currentPage, language}: {currentPage: string; language: "en" | "ar"}) { export default function Navbar({
const [isOpen, setIsOpen] = useState(false); currentPage,
language,
}: {
currentPage: string;
language: "en" | "ar";
}) {
const [isOpen, setIsOpen] = useState(false);
return ( return (
<> <>
<header className="-md:hidden w-full items-center justify-between px-11 py-3 shadow-sm md:flex"> <header className="-md:hidden w-full items-center justify-between px-11 py-3 shadow-sm md:flex">
<Link href="/"> <Link href="/">
<Image src="/logo_title.png" alt="EnCoach logo" width={128} height={128} /> <Image
</Link> src="/logo_title.png"
<div className={clsx("flex w-fit items-center gap-8", language === "ar" && "flex-row-reverse")}> alt="EnCoach logo"
{items.map((item) => ( width={128}
<Link height={128}
key={item.key} />
href={language === "ar" ? `/${language}${item.page}` : item.page} </Link>
className={clsx( <div
"hover:border-b-mti-purple-light transition duration-300 ease-in-out hover:border-b-2", className={clsx(
currentPage === item.page && "border-b-mti-purple-light border-b-2", "flex w-fit items-center gap-8",
)}> language === "ar" && "flex-row-reverse"
{(translation as any)[item.key][language]} )}
</Link> >
))} {items.map((item) =>
</div> item.entries ? (
<div className="flex w-fit items-center gap-4"> <NestedNavbarEntry
<Link key={item.key}
href="https://platform.encoach.com" entry={item}
className="hover:bg-mti-purple-dark border-mti-purple-dark rounded-xl border px-8 py-2 transition duration-300 ease-in-out hover:text-white"> language={language}
{translation.platform[language]} translation={translation}
</Link> />
<Link ) : (
href="https://platform.encoach.com/register" <Link
className="hover:bg-mti-purple-dark hover:border-mti-purple-dark border-mti-purple-light bg-mti-purple-light rounded-xl border px-8 py-2 text-white transition duration-300 ease-in-out"> key={item.key}
{translation.join[language]} href={
</Link> language === "ar" ? `/${language}${item.page}` : item.page
{language === "ar" ? ( }
<Link className={clsx(
className="text-mti-purple-light hover:text-mti-purple-dark transition duration-300 ease-in-out" "hover:border-b-mti-purple-light transition duration-300 ease-in-out hover:border-b-2",
href={`${currentPage}`}> currentPage === item.page &&
EN "border-b-mti-purple-light border-b-2"
</Link> )}
) : ( >
<Link {(translation as any)[item.key][language]}
className="text-mti-purple-light hover:text-mti-purple-dark transition duration-300 ease-in-out" </Link>
href={`/ar${currentPage}`}> )
AR )}
</Link> </div>
)} <div className="flex w-fit items-center gap-4">
</div> <Link
</header> href="https://platform.encoach.com"
className="hover:bg-mti-purple-dark border-mti-purple-dark rounded-xl border px-8 py-2 transition duration-300 ease-in-out hover:text-white"
>
{translation.platform[language]}
</Link>
<Link
href="https://platform.encoach.com/register"
className="hover:bg-mti-purple-dark hover:border-mti-purple-dark border-mti-purple-light bg-mti-purple-light rounded-xl border px-8 py-2 text-white transition duration-300 ease-in-out"
>
{translation.join[language]}
</Link>
{language === "ar" ? (
<Link
className="text-mti-purple-light hover:text-mti-purple-dark transition duration-300 ease-in-out"
href={`${currentPage}`}
>
EN
</Link>
) : (
<Link
className="text-mti-purple-light hover:text-mti-purple-dark transition duration-300 ease-in-out"
href={`/ar${currentPage}`}
>
AR
</Link>
)}
</div>
</header>
<Transition appear show={isOpen} as={Fragment}> <Transition appear show={isOpen} as={Fragment}>
<Dialog as="div" className="relative z-10" onClose={() => setIsOpen(false)}> <Dialog
<Transition.Child as="div"
as={Fragment} className="relative z-10"
enter="ease-out duration-300" onClose={() => setIsOpen(false)}
enterFrom="opacity-0" >
enterTo="opacity-100" <Transition.Child
leave="ease-in duration-200" as={Fragment}
leaveFrom="opacity-100" enter="ease-out duration-300"
leaveTo="opacity-0"> enterFrom="opacity-0"
<div className="fixed inset-0 bg-black bg-opacity-25" /> enterTo="opacity-100"
</Transition.Child> leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-black bg-opacity-25" />
</Transition.Child>
<div className="fixed inset-0 overflow-y-auto"> <div className="fixed inset-0 overflow-y-auto">
<div className="flex min-h-full items-center justify-center text-center"> <div className="flex min-h-full items-center justify-center text-center">
<Transition.Child <Transition.Child
as={Fragment} as={Fragment}
enter="ease-out duration-300" enter="ease-out duration-300"
enterFrom="opacity-0 scale-95" enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100" enterTo="opacity-100 scale-100"
leave="ease-in duration-200" leave="ease-in duration-200"
leaveFrom="opacity-100 scale-100" leaveFrom="opacity-100 scale-100"
leaveTo="opacity-0 scale-95"> leaveTo="opacity-0 scale-95"
<Dialog.Panel className="flex h-screen w-full transform flex-col gap-8 overflow-hidden bg-white text-left align-middle text-black shadow-xl transition-all"> >
<Dialog.Title as="header" className="-md:flex w-full items-center justify-between px-8 py-2 shadow-sm md:hidden"> <Dialog.Panel className="flex h-screen w-full transform flex-col gap-8 overflow-hidden bg-white text-left align-middle text-black shadow-xl transition-all">
<Link href="/"> <Dialog.Title
<Image src="/logo_title.png" alt="EnCoach logo" width={128} height={128} /> as="header"
</Link> className="-md:flex w-full items-center justify-between px-8 py-2 shadow-sm md:hidden"
<div className="flex items-center gap-4"> >
{language === "ar" ? ( <Link href="/">
<Link <Image
className="text-mti-purple-light hover:text-mti-purple-dark transition duration-300 ease-in-out" src="/logo_title.png"
href={`${currentPage}`}> alt="EnCoach logo"
EN width={128}
</Link> height={128}
) : ( />
<Link </Link>
className="text-mti-purple-light hover:text-mti-purple-dark transition duration-300 ease-in-out" <div className="flex items-center gap-4">
href={`/ar${currentPage}`}> {language === "ar" ? (
AR <Link
</Link> className="text-mti-purple-light hover:text-mti-purple-dark transition duration-300 ease-in-out"
)} href={`${currentPage}`}
<div className="cursor-pointer" onClick={() => setIsOpen(false)} tabIndex={0}> >
<BsXLg className="text-mti-purple-light text-2xl" onClick={() => setIsOpen(false)} /> EN
</div> </Link>
</div> ) : (
</Dialog.Title> <Link
<div className={clsx("flex flex-col gap-6 px-8 text-lg", language === "ar" && "items-end")}> className="text-mti-purple-light hover:text-mti-purple-dark transition duration-300 ease-in-out"
{items.map((item) => ( href={`/ar${currentPage}`}
<Link >
key={item.key} AR
href={language === "ar" ? `/${language}${item.page}` : item.page} </Link>
className={clsx( )}
"w-fit transition duration-300 ease-in-out", <div
currentPage === item.page && className="cursor-pointer"
"text-mti-purple-light border-b-mti-purple-light border-b-2 font-semibold ", onClick={() => setIsOpen(false)}
)}> tabIndex={0}
{(translation as any)[item.key][language]} >
</Link> <BsXLg
))} className="text-mti-purple-light text-2xl"
<Link onClick={() => setIsOpen(false)}
href="https://platform.encoach.com/register" />
className={clsx( </div>
"w-fit transition duration-300 ease-in-out", </div>
currentPage === "/join" && </Dialog.Title>
"text-mti-purple-light border-b-mti-purple-light border-b-2 font-semibold ", <div
)}> className={clsx(
{translation.join[language]} "flex flex-col gap-6 px-8 text-lg",
</Link> language === "ar" && "items-end"
<Link href="https://platform.encoach.com" className={clsx("w-fit transition duration-300 ease-in-out")}> )}
{translation.platform[language]} >
</Link> {items.map((item) => (
</div> <Link
</Dialog.Panel> key={item.key}
</Transition.Child> href={
</div> language === "ar"
</div> ? `/${language}${item.page}`
</Dialog> : item.page
</Transition> }
className={clsx(
"w-fit transition duration-300 ease-in-out",
currentPage === item.page &&
"text-mti-purple-light border-b-mti-purple-light border-b-2 font-semibold "
)}
>
{(translation as any)[item.key][language]}
</Link>
))}
<Link
href="https://platform.encoach.com/register"
className={clsx(
"w-fit transition duration-300 ease-in-out",
currentPage === "/join" &&
"text-mti-purple-light border-b-mti-purple-light border-b-2 font-semibold "
)}
>
{translation.join[language]}
</Link>
<Link
href="https://platform.encoach.com"
className={clsx(
"w-fit transition duration-300 ease-in-out"
)}
>
{translation.platform[language]}
</Link>
</div>
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</Dialog>
</Transition>
<header className="-md:flex w-full items-center justify-between px-8 py-2 md:hidden"> <header className="-md:flex w-full items-center justify-between px-8 py-2 md:hidden">
<Link href="/"> <Link href="/">
<Image src="/logo_title.png" alt="EnCoach logo" width={69} height={69} /> <Image
</Link> src="/logo_title.png"
<div className="flex items-center gap-4"> alt="EnCoach logo"
{language === "ar" ? ( width={69}
<Link height={69}
className="text-mti-purple-light hover:text-mti-purple-dark transition duration-300 ease-in-out" />
href={`${currentPage}`}> </Link>
EN <div className="flex items-center gap-4">
</Link> {language === "ar" ? (
) : ( <Link
<Link className="text-mti-purple-light hover:text-mti-purple-dark transition duration-300 ease-in-out"
className="text-mti-purple-light hover:text-mti-purple-dark transition duration-300 ease-in-out" href={`${currentPage}`}
href={`/ar${currentPage}`}> >
AR EN
</Link> </Link>
)} ) : (
<div className="cursor-pointer" onClick={() => setIsOpen(true)}> <Link
<BsList className="text-2xl" onClick={() => setIsOpen(true)} /> className="text-mti-purple-light hover:text-mti-purple-dark transition duration-300 ease-in-out"
</div> href={`/ar${currentPage}`}
</div> >
</header> AR
</> </Link>
); )}
<div className="cursor-pointer" onClick={() => setIsOpen(true)}>
<BsList className="text-2xl" onClick={() => setIsOpen(true)} />
</div>
</div>
</header>
</>
);
} }

View File

@@ -0,0 +1,76 @@
import React from "react";
import clsx from "clsx";
interface NavbarNestedEntry {
name: string;
number: string;
email: string;
}
interface NavbarEntry {
key: string;
entries: {
key: string;
entries: NavbarNestedEntry[];
}[];
}
const NestedSubmenus = ({ entry }: { entry: NavbarEntry }) => {
const [open, setOpen] = React.useState<string | null>(null);
return (
<ul className="absolute hidden group-hover:block bg-gray-700 text-white rounded shadow-md mt-1">
{entry.entries.map((entry) => (
<li key={entry.key} className="group relative">
<div
className="px-4 py-2 hover:bg-gray-600 whitespace-nowrap h-12 flex items-center"
onMouseEnter={() => setOpen(entry.key)}
onMouseLeave={() => setOpen(null)}
>
<span>{entry.key}</span>
{open === entry.key && (
<ul className="absolute hidden group-hover:block bg-gray-800 text-white rounded shadow-md left-full top-0">
{entry.entries.map((value) => (
<li
key={value.name}
className="px-4 py-2 hover:bg-gray-700 whitespace-nowrap h-12 flex items-center"
>
<span>{value.name}</span>
</li>
))}
</ul>
)}
</div>
</li>
))}
</ul>
);
};
const NestedNavbarEntry = ({
entry,
language,
translation
}: {
entry: NavbarEntry;
language: "en" | "ar";
translation: any
}) => {
const [open, setOpen] = React.useState<string | null>(null);
return (
<div
className={clsx(
"group relative hover:border-b-mti-purple-light transition duration-300 ease-in-out hover:border-b-2 z-10 cursor-pointer",
open === entry.key && "border-b-mti-purple-light border-b-2"
)}
onMouseEnter={() => setOpen(entry.key)}
onMouseLeave={() => setOpen(null)}
>
<span className="py-2">
{translation[entry.key][language]}
</span>
<NestedSubmenus entry={entry} />
</div>
);
};
export default NestedNavbarEntry;

View File

@@ -30,5 +30,9 @@
"join": { "join": {
"en": "Join", "en": "Join",
"ar": "انضم إلينا" "ar": "انضم إلينا"
},
"country_manager": {
"en": "Country Manager",
"ar": "المدير المحلي"
} }
} }