Update the query and ui to show the subitem of the menu

This commit is contained in:
Mandeep Tatla 2024-06-07 16:01:48 +09:30
parent c799a5f53d
commit 6f16b74580
7 changed files with 130 additions and 59 deletions

View File

@ -0,0 +1,20 @@
import { MenuItem } from '../../lib/shopify/types';
import MenuItemComponent from './menu-item-component';
type MegaMenuProps = {
menu: MenuItem[];
};
const MegaMenu = ({ menu }: MegaMenuProps) => {
return (
<div className="mt-4 hidden justify-center md:flex">
<ul className="text-md flex gap-6 font-semibold">
{menu.map((item) => (
<MenuItemComponent key={item.title} item={item} />
))}
</ul>
</div>
);
};
export default MegaMenu;

View File

@ -0,0 +1,38 @@
import Link from 'next/link';
import { MenuItem } from '../../lib/shopify/types';
type MenuItemProps = {
item: MenuItem;
isSubMenu?: boolean;
};
const MenuItemComponent = ({ item, isSubMenu = false }: MenuItemProps) => {
return (
<li className={`group relative ${isSubMenu ? 'submenu-item p-2' : ''}`}>
<Link
href={item.path}
className="pl-2 font-semibold text-neutral-500 underline-offset-4 hover:text-black hover:underline dark:text-neutral-400 dark:hover:text-neutral-300"
>
{item.title}
</Link>
{item.items.length > 0 && (
<ul className="absolute left-0 top-full z-10 hidden w-48 rounded-xl bg-white shadow-lg group-hover:block">
{item.items.map((subItem) => (
<MenuItemComponent key={subItem.title} item={subItem} isSubMenu={true} />
))}
</ul>
)}
{isSubMenu && item.items.length > 0 && (
<ul className="absolute left-0 top-full z-10 hidden w-48 bg-white shadow-lg group-hover:block">
{item.items.map((subItem, index) =>
index === 2 ? (
<MenuItemComponent key={subItem.title} item={subItem} isSubMenu={true} />
) : null
)}
</ul>
)}
</li>
);
};
export default MenuItemComponent;

View File

@ -1,9 +1,8 @@
import MegaMenu from 'components/MegaMenu/mega-meu';
import Cart from 'components/cart'; import Cart from 'components/cart';
import OpenCart from 'components/cart/open-cart'; import OpenCart from 'components/cart/open-cart';
import LogoSquare from 'components/logo-square'; import LogoSquare from 'components/logo-square';
import { getMenu } from 'lib/shopify'; import { getMegaMenu } from 'lib/shopify';
import { Menu } from 'lib/shopify/types';
import { RemoveTheDomainFromUrl } from 'lib/utils';
import Link from 'next/link'; import Link from 'next/link';
import { Suspense } from 'react'; import { Suspense } from 'react';
import MobileMenu from './mobile-menu'; import MobileMenu from './mobile-menu';
@ -11,49 +10,41 @@ import Search, { SearchSkeleton } from './search';
const { SITE_NAME } = process.env; const { SITE_NAME } = process.env;
export default async function Navbar() { export default async function Navbar() {
const menu = await getMenu('next-js-frontend-header-menu'); const menu = await getMegaMenu('next-js-frontend-header-menu');
return ( return (
<nav className="relative flex items-center justify-between p-4 lg:px-6"> <nav className="relative mx-auto max-w-screen-2xl p-4 lg:px-6">
<div className="block flex-none md:hidden"> <div className="flex items-center justify-between">
<Suspense fallback={null}> <div className="block flex-none md:hidden">
<MobileMenu menu={menu} /> <Suspense fallback={null}>
</Suspense> <MobileMenu menu={menu} />
</div>
<div className="flex w-full items-center">
<div className="flex w-full md:w-1/3">
<Link href="/" className="mr-2 flex w-full items-center justify-center md:w-auto lg:mr-6">
<LogoSquare />
<div className="ml-2 flex-none text-sm font-medium uppercase md:hidden lg:block">
{SITE_NAME}
</div>
</Link>
{menu.length ? (
<ul className="hidden gap-6 text-sm md:flex md:items-center">
{menu.map((item: Menu) => (
<li key={item.title}>
<Link
href={RemoveTheDomainFromUrl(item.path)}
className="text-neutral-500 underline-offset-4 hover:text-black hover:underline dark:text-neutral-400 dark:hover:text-neutral-300"
>
{item.title}
</Link>
</li>
))}
</ul>
) : null}
</div>
<div className="hidden justify-center md:flex md:w-1/3">
<Suspense fallback={<SearchSkeleton />}>
<Search />
</Suspense> </Suspense>
</div> </div>
<div className="flex justify-end md:w-1/3"> <div className="flex w-full items-center">
<Suspense fallback={<OpenCart />}> <div className="flex w-full md:w-1/3">
<Cart /> <Link
</Suspense> href="/"
className="mr-2 flex w-full items-center justify-center md:w-auto lg:mr-6"
>
<LogoSquare />
<div className="ml-2 flex-none text-sm font-medium uppercase md:hidden lg:block">
{SITE_NAME}
</div>
</Link>
</div>
<div className="hidden justify-center md:flex md:w-1/3">
<Suspense fallback={<SearchSkeleton />}>
<Search />
</Suspense>
</div>
<div className="flex justify-end md:w-1/3">
<Suspense fallback={<OpenCart />}>
<Cart />
</Suspense>
</div>
</div> </div>
</div> </div>
{menu.length ? <MegaMenu menu={menu} /> : null}
</nav> </nav>
); );
} }

View File

@ -29,6 +29,7 @@ import {
Connection, Connection,
Image, Image,
Menu, Menu,
MenuItem,
Page, Page,
Product, Product,
ShopifyAddToCartOperation, ShopifyAddToCartOperation,
@ -354,6 +355,30 @@ export async function getMenu(handle: string): Promise<Menu[]> {
); );
} }
export async function getMegaMenu(handle: string): Promise<MenuItem[]> {
const res = await shopifyFetch<ShopifyMenuOperation>({
query: getMenuQuery,
tags: [TAGS.collections],
variables: {
handle
}
});
// Function to map the menu items and their nested items
const mapMenuItems = (items: any): MenuItem[] => {
console.log(items);
return items.map((item: any) => ({
title: item.title,
path: item.url
? item.url.replace(domain, '').replace('/collections', '/search').replace('/pages', '')
: '',
items: item.items ? mapMenuItems(item.items) : []
}));
};
return res.body?.data?.menu?.items ? mapMenuItems(res.body.data.menu.items) : [];
}
export async function getPage(handle: string): Promise<Page> { export async function getPage(handle: string): Promise<Page> {
const res = await shopifyFetch<ShopifyPageOperation>({ const res = await shopifyFetch<ShopifyPageOperation>({
query: getPageQuery, query: getPageQuery,

View File

@ -4,6 +4,14 @@ export const getMenuQuery = /* GraphQL */ `
items { items {
title title
url url
items {
title
url
items {
title
url
}
}
} }
} }
} }

View File

@ -45,6 +45,14 @@ export type Menu = {
path: string; path: string;
}; };
export interface MenuItem {
title: string;
path: string;
items: MenuItem[];
}
export type MegaMenu = MenuItem[];
export type Money = { export type Money = {
amount: string; amount: string;
currencyCode: string; currencyCode: string;

View File

@ -1,5 +1,4 @@
import { ReadonlyURLSearchParams } from 'next/navigation'; import { ReadonlyURLSearchParams } from 'next/navigation';
const { SHOPIFY_STORE_DOMAIN } = process.env;
export const createUrl = (pathname: string, params: URLSearchParams | ReadonlyURLSearchParams) => { export const createUrl = (pathname: string, params: URLSearchParams | ReadonlyURLSearchParams) => {
const paramsString = params.toString(); const paramsString = params.toString();
@ -8,24 +7,6 @@ export const createUrl = (pathname: string, params: URLSearchParams | ReadonlyUR
return `${pathname}${queryString}`; return `${pathname}${queryString}`;
}; };
export const RemoveTheDomainFromUrl = (url: string) => {
const lowercaseString = `https://${SHOPIFY_STORE_DOMAIN}`.toLowerCase();
const modifiedUrl = url.replace(lowercaseString, '');
return modifiedUrl;
};
export const RemoveTheDomainFromArray = (menu: any) => {
const lowercaseString = `https://${SHOPIFY_STORE_DOMAIN}`.toLowerCase();
const modifiedUrls = menu.map((item: any) => {
const modifiedPath = item.path.replace(lowercaseString, '');
return { ...item, path: modifiedPath };
});
return modifiedUrls;
};
export const ensureStartsWith = (stringToCheck: string, startsWith: string) => export const ensureStartsWith = (stringToCheck: string, startsWith: string) =>
stringToCheck.startsWith(startsWith) ? stringToCheck : `${startsWith}${stringToCheck}`; stringToCheck.startsWith(startsWith) ? stringToCheck : `${startsWith}${stringToCheck}`;