feat: implement new footer

Signed-off-by: Chloe <pinkcloudvnn@gmail.com>
This commit is contained in:
Chloe 2024-04-16 16:53:46 +07:00
parent 5c59c86d55
commit b08cf040ce
No known key found for this signature in database
GPG Key ID: CFD53CE570D42DF5
14 changed files with 90 additions and 19 deletions

View File

@ -19,7 +19,7 @@ const FooterMenuItem = ({ item }: { item: Menu }) => {
<Link
href={item.path}
className={clsx(
'block p-2 text-lg underline-offset-4 hover:text-black hover:underline md:inline-block md:text-sm dark:hover:text-neutral-300',
'block py-2 text-lg underline-offset-4 hover:underline md:inline-block md:text-sm',
{
'text-black dark:text-neutral-300': active
}
@ -31,16 +31,31 @@ const FooterMenuItem = ({ item }: { item: Menu }) => {
);
};
export default function FooterMenu({ menu }: { menu: Menu[] }) {
function FooterMenu({ menu }: { menu: Menu[] }) {
if (!menu.length) return null;
return (
<nav>
<ul>
{menu.map((item: Menu) => {
return <FooterMenuItem key={item.title} item={item} />;
})}
</ul>
);
}
export default function FooterMenuGrid({ menu }: { menu: Menu[] }) {
if (!menu.length) return null;
return (
<nav className="ml-2 flex lg:ml-auto">
<div className="grid w-full grid-cols-2 gap-0 md:grid-cols-3 lg:gap-4">
{menu.map((item) => (
<div key={item.title}>
<span className="text-primary">{item.title}</span>
<FooterMenu menu={item.items} />
</div>
))}
</div>
</nav>
);
}

View File

@ -3,6 +3,7 @@ import Link from 'next/link';
import FooterMenu from 'components/layout/footer-menu';
import LogoSquare from 'components/logo-square';
import { getMenu } from 'lib/shopify';
import Image from 'next/image';
import { Suspense } from 'react';
const { COMPANY_NAME, SITE_NAME } = process.env;
@ -11,17 +12,49 @@ export default async function Footer() {
const currentYear = new Date().getFullYear();
const copyrightDate = 2023 + (currentYear > 2023 ? `-${currentYear}` : '');
const skeleton = 'w-full h-6 animate-pulse rounded bg-neutral-200 dark:bg-neutral-700';
const menu = await getMenu('next-js-frontend-footer-menu');
const menu = await getMenu('footer');
const copyrightName = COMPANY_NAME || SITE_NAME || '';
return (
<footer className="text-sm text-neutral-500 dark:text-neutral-400">
<div className="mx-auto flex w-full max-w-7xl flex-col gap-6 border-t border-neutral-200 px-6 py-12 text-sm md:flex-row md:gap-12 md:px-4 min-[1320px]:px-0 dark:border-neutral-700">
<div>
<Link className="flex items-center text-black md:pt-1 dark:text-white" href="/">
<footer className="bg-dark text-sm text-white">
<div className="mx-auto flex w-full max-w-7xl flex-row flex-wrap items-start gap-6 px-6 py-12 text-sm md:gap-12 md:px-4 min-[1320px]:px-0">
<div className="flex flex-col gap-1">
<Link className="flex items-center text-white md:pt-1" href="/">
<LogoSquare />
<span className="uppercase">{SITE_NAME}</span>
</Link>
<a href={`tel:${8882422605}`} className="ml-4 text-white">
(888) 242-2605
</a>
<p className="ml-4">Monday - Friday 9:00am - 8:00pm EST</p>
<p className="ml-4">Saturday 11:00am - 4:00pm EST</p>
<div className="ml-4 mt-3 flex flex-row items-center gap-4">
<a href="https://www.facebook.com/carpartplanet" target="_blank" rel="noreferrer">
<Image alt="facebook" src="/icons/facebook.png" width={20} height={20} />
</a>
<a href="https://www.instagram.com/carpartplanet" target="_blank" rel="noreferrer">
<Image alt="instagram" src="/icons/instagram.png" width={20} height={20} />
</a>
<a
href="https://www.youtube.com/channel/UC8CxAf0QAozd2g0clMhkmKA/videos"
target="_blank"
rel="noreferrer"
>
<Image
alt="youtube"
src="/icons/youtube.png"
width={20}
height={20}
className="mt-1"
/>
</a>
<a href="https://www.pinterest.com/carpartplanet" target="_blank" rel="noreferrer">
<Image alt="pinterest" src="/icons/pinterest.png" width={20} height={20} />
</a>
<a href="https://twitter.com/carpartplanet" target="_blank" rel="noreferrer">
<Image alt="twitter" src="/icons/twitter.png" width={16} height={16} />
</a>
</div>
</div>
<Suspense
fallback={
@ -44,6 +77,16 @@ export default async function Footer() {
&copy; {copyrightDate} {copyrightName}
{copyrightName.length && !copyrightName.endsWith('.') ? '.' : ''} All rights reserved.
</p>
<div className="ml-0 flex flex-row items-center gap-2 md:ml-auto">
<Image alt="visa" src="/icons/visa.png" width={30} height={20} />
<Image alt="mastercard" src="/icons/mastercard.png" width={30} height={20} />
<Image
alt="american-express"
src="/icons/american-express.png"
width={30}
height={20}
/>
</div>
</div>
</div>
</footer>

View File

@ -1,6 +1,6 @@
import { HIDDEN_PRODUCT_TAG, SHOPIFY_GRAPHQL_API_ENDPOINT, TAGS } from 'lib/constants';
import { isShopifyError } from 'lib/type-guards';
import { ensureStartsWith } from 'lib/utils';
import { ensureStartsWith, normalizeUrl } from 'lib/utils';
import { revalidateTag } from 'next/cache';
import { headers } from 'next/headers';
import { NextRequest, NextResponse } from 'next/server';
@ -346,12 +346,16 @@ export async function getMenu(handle: string): Promise<Menu[]> {
}
});
return (
res.body?.data?.menu?.items.map((item: { title: string; url: string }) => ({
const formatMenuItems = (
menu: { title: string; url: string; items?: { title: string; url: string }[] }[] = []
): Menu[] =>
menu.map((item) => ({
title: item.title,
path: item.url.replace(domain, '').replace('/collections', '/search').replace('/pages', '')
})) || []
);
path: normalizeUrl(domain, item.url),
items: item.items?.length ? formatMenuItems(item.items) : []
}));
return formatMenuItems(res.body?.data?.menu?.items);
}
export async function getPage(handle: string): Promise<Page> {

View File

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

View File

@ -43,6 +43,7 @@ export type Image = {
export type Menu = {
title: string;
path: string;
items: Menu[];
};
export type Money = {

View File

@ -43,3 +43,7 @@ export const validateEnvironmentVariables = () => {
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
export function normalizeUrl(domain: string, url: string) {
return url.replace(domain, '').replace('/collections', '/search').replace('/pages', '');
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
public/icons/facebook.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
public/icons/instagram.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
public/icons/mastercard.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
public/icons/pinterest.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
public/icons/twitter.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
public/icons/visa.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
public/icons/youtube.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB