feat: add footer components including FooterInfo, FooterMenuBuilder, and FooterMenuBuilderWrapper

- Implemented FooterInfo component to display logo, slogan, and social links.
- Created FooterMenuBuilder component for rendering a list of links with a title.
- Added FooterMenuBuilderWrapper to encapsulate the layout for menu items.
This commit is contained in:
2025-06-02 18:06:15 +03:30
parent 86dc740dfd
commit 919364a4dd
15 changed files with 242 additions and 16 deletions
+5 -1
View File
@@ -1,3 +1,4 @@
import Footer from '@/components/layout/footer/Footer';
import './globals.css'; import './globals.css';
import { cookies } from 'next/headers'; import { cookies } from 'next/headers';
@@ -16,7 +17,10 @@ export default async function RootLayout({
return ( return (
<html lang={locale} dir={locale === 'fa' ? 'rtl' : 'ltr'}> <html lang={locale} dir={locale === 'fa' ? 'rtl' : 'ltr'}>
<body className="min-h-svh w-full overflow-x-hidden">{children}</body> <body className="min-h-svh w-full overflow-x-hidden">
{children}
<Footer />
</body>
</html> </html>
); );
} }
+2
View File
@@ -1,5 +1,7 @@
import { quickLinks } from './quickLinks.const'; import { quickLinks } from './quickLinks.const';
import { whoAreWeLinks } from './whoAreWe.const';
export const footerMenu = { export const footerMenu = {
quickLinks, quickLinks,
whoAreWeLinks,
}; };
@@ -1,7 +1,16 @@
import { appConstants } from '..'; import routeFactory from '../routeFactory';
export const quickLinks = [ export const quickLinks = [
{ {
title: appConstants.routeFactory.home.title, title: routeFactory.home.title,
link: routeFactory.home.route(),
},
{
title: routeFactory.products.title,
link: routeFactory.products.route(),
},
{
title: routeFactory.projects.title,
link: routeFactory.projects.route(),
}, },
] as any[]; ] as any[];
@@ -0,0 +1,12 @@
import routeFactory from '../routeFactory';
export const whoAreWeLinks = [
{
title: routeFactory.about.title,
link: routeFactory.about.route(),
},
{
title: routeFactory.contact.title,
link: routeFactory.contact.route(),
},
] as any[];
+2
View File
@@ -1,7 +1,9 @@
import { footerMenu } from './footerMenu'; import { footerMenu } from './footerMenu';
import routeFactory from './routeFactory'; import routeFactory from './routeFactory';
import socials from './socials';
export const appConstants = { export const appConstants = {
routeFactory, routeFactory,
footerMenu, footerMenu,
socials,
}; };
@@ -5,8 +5,20 @@ export default {
title: t('pages_home_title'), title: t('pages_home_title'),
route: () => '/', route: () => '/',
}, },
products: {
title: t('page_products_title'),
route: () => '/products',
},
projects: {
title: t('page_projects_title'),
route: () => '/projects',
},
about: { about: {
title: t('pages_about_title'), title: t('pages_about_title'),
route: () => '/about-us', route: () => '/about-us',
}, },
contact: {
title: t('page_contact_title'),
route: () => '/contact',
},
}; };
+6
View File
@@ -0,0 +1,6 @@
export default [
{
title: 'instagram',
link: 'https://www.instagram.com/yourprofile',
},
];
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

+2
View File
@@ -4,6 +4,7 @@ import aboutBanner from './about_banner.png';
import factoryVector from './factory_vector.png'; import factoryVector from './factory_vector.png';
import aboutImg_1 from './about-img-1.png'; import aboutImg_1 from './about-img-1.png';
import aboutImg_2 from './about-img-2.png'; import aboutImg_2 from './about-img-2.png';
import homeProductBack from './home_product_back.jpeg';
export default { export default {
homeBanner, homeBanner,
@@ -12,4 +13,5 @@ export default {
factoryVector, factoryVector,
aboutImg_1, aboutImg_1,
aboutImg_2, aboutImg_2,
homeProductBack,
}; };
+77
View File
@@ -0,0 +1,77 @@
import images from '@/assets/images/images';
import { t } from '@/locales/translates';
import Image from 'next/image';
import Link from 'next/link';
import FooterMenuBuilder from './FooterMenuBuilder';
import { appConstants } from '@/assets/constants';
import FooterMenuBuilderWrapper from './FooterMenuBuilderWrapper';
import FooterInfo from './FooterInfo';
export default function Footer() {
return (
<div className="relative w-full bg-gray-500 pt-24 text-white">
<div className="absolute inset-0 z-10 flex items-end justify-start">
<div className="w-1/2 md:w-2/5">
<Image
alt="footer-bg"
src={images.factoryVector}
className="h-full w-full object-cover"
/>
</div>
</div>
<div className="relative z-20 container mx-auto">
<div className="flex max-w-[920px] flex-col gap-5 pb-10">
<p className="text-5xl tracking-tighter">
{t('footer_title') as string}
</p>
<h5 className="text-base tracking-normal">
{t('footer_description') as string}
</h5>
</div>
<div className="flex items-start justify-between gap-8 border-y border-gray-300/50 py-14 max-lg:flex-col-reverse">
<FooterInfo withLogo className="lg:max-w-[200px]" />
<div className="grid w-full grid-cols-2 gap-5 md:grid-cols-3 md:gap-10 lg:max-w-3/5">
<FooterMenuBuilder
title={t('footer_links_quick_links') as string}
links={appConstants.footerMenu.quickLinks}
className="max-md:text-center"
/>
<FooterMenuBuilder
title={t('footer_links_who_we_are') as string}
links={appConstants.footerMenu.whoAreWeLinks}
className="max-md:text-center"
/>
<FooterMenuBuilderWrapper
title={t('footer_links_contact') as string}
className="max-md:col-span-2 max-md:text-center"
>
<div className="flex flex-col gap-4">
<Link
href="tel:+9812345678"
className="text-sm tracking-tight text-gray-100 hover:text-gray-200"
>
+98 12345678
</Link>
<Link
href="mailto:info@sample.com"
className="text-sm tracking-tight text-gray-100 hover:text-gray-200"
>
info@sample.com
</Link>
<span className="text-sm tracking-tight text-gray-100 hover:text-gray-200">
{t('footer_links_address') as string}
</span>
</div>
</FooterMenuBuilderWrapper>
</div>
</div>
<div className="py-8 lg:py-14">
<p className="text-center text-sm">
{t('footer_copyright') as string}
</p>
</div>
</div>
</div>
);
}
@@ -0,0 +1,45 @@
import { appConstants } from '@/assets/constants';
import images from '@/assets/images/images';
import { t } from '@/locales/translates';
import Image from 'next/image';
import Link from 'next/link';
export default function FooterInfo({
withLogo,
className,
}: {
withLogo?: boolean;
className?: string;
}) {
return (
<div className={`flex w-full flex-col gap-3 lg:w-auto ${className}`}>
{withLogo && (
<Link href="/" className="mx-auto h-8 w-auto">
<Image
alt="logo"
src={images.logo}
className="h-full w-auto object-contain"
/>
</Link>
)}
<p className="text-sm tracking-normal">{t('footer_slogan') as string}</p>
<div className="flex items-center gap-4 max-lg:justify-center">
{appConstants.socials.map((social) => (
<Link
href={social.link}
key={social.title}
className="text-white lg:flex-1"
>
{/* <Image
alt={social.title}
src={images[social.title]}
className="h-6 w-6 object-contain"
/> */}
{social.title}
</Link>
))}
</div>
</div>
);
}
@@ -0,0 +1,29 @@
import Link from 'next/link';
import FooterMenuBuilderWrapper from './FooterMenuBuilderWrapper';
export default function FooterMenuBuilder({
title,
links,
className = '',
}: {
title: string;
links: { title: string; link: string }[];
className?: string;
}) {
return (
<FooterMenuBuilderWrapper title={title} className={className}>
<div className="flex flex-col gap-4">
{links.map((link) => (
<Link
title={link.title}
href={link.link}
className="text-sm tracking-tight text-gray-100 hover:text-gray-200"
key={link.title}
>
{link.title}
</Link>
))}
</div>
</FooterMenuBuilderWrapper>
);
}
@@ -0,0 +1,16 @@
export default function FooterMenuBuilderWrapper({
title,
children,
className,
}: {
title: string;
children: React.ReactNode;
className: string;
}) {
return (
<div className={`flex flex-col gap-6 ${className}`}>
<span className="text-lg tracking-tight">{title}</span>
{children}
</div>
);
}
+1 -1
View File
@@ -26,7 +26,7 @@ export default function HomePageProducts() {
<div className="w-full"> <div className="w-full">
<div <div
className={`relative w-full bg-cover bg-fixed bg-center bg-no-repeat pt-28 pb-48`} className={`relative w-full bg-cover bg-fixed bg-center bg-no-repeat pt-28 pb-48`}
style={{ backgroundImage: `url(${images.homeBanner.src})` }} style={{ backgroundImage: `url(${images.homeProductBack.src})` }}
> >
<div className="absolute inset-0 z-10 bg-black opacity-50" /> <div className="absolute inset-0 z-10 bg-black opacity-50" />
<div className="relative z-20 container mx-auto grid grid-cols-2 gap-28"> <div className="relative z-20 container mx-auto grid grid-cols-2 gap-28">
+15 -5
View File
@@ -10,16 +10,13 @@ const en = {
pages_contact_description: 'Get in touch with us through this page', pages_contact_description: 'Get in touch with us through this page',
com_home_banner_title: 'Excellence innovating', com_home_banner_title: 'Excellence innovating',
com_home_banner_title_bold: 'industry for today', com_home_banner_title_bold: 'industry for today',
com_home_banner_description:'At the heart of our operations is a commitment to delivering superior products through cutting- <br/> edge technology and innovative processes. ', com_home_banner_description:
'At the heart of our operations is a commitment to delivering superior products through cutting- <br/> edge technology and innovative processes. ',
com_home_banner_button: ' Explore More', com_home_banner_button: ' Explore More',
com_home_Banner_link1: ' Advanced Manufacturing Solutions', com_home_Banner_link1: ' Advanced Manufacturing Solutions',
com_home_Banner_link2: 'Quality Assurance Systems', com_home_Banner_link2: 'Quality Assurance Systems',
com_home_Banner_link3: 'State-of-the-Art Technology', com_home_Banner_link3: 'State-of-the-Art Technology',
com_about_info_title: 'Building quality through', com_about_info_title: 'Building quality through',
com_about_info_title_bold: 'industrial innovation', com_about_info_title_bold: 'industrial innovation',
com_about_info_description: com_about_info_description:
@@ -35,6 +32,19 @@ const en = {
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. ', 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. ',
page_products_title: 'Products', page_products_title: 'Products',
page_contact_title: 'Contact Us',
page_projects_title: 'Projects',
footer_title: 'Industrial Solutions for a Modern World',
footer_description:
'We are committed to providing personalized industrial solutions that meet the unique needs of our clients. Our team of experts is dedicated to delivering high-quality products and services that drive success in todays competitive market.',
footer_slogan:
'We are committed to providing personalized industrial solutions.',
footer_links_quick_links: 'Quick Links',
footer_links_who_we_are: 'Who We Are',
footer_links_contact: 'Contact',
footer_copyright: 'Copyright © 2025 All Rights Reserved.',
footer_links_address: '123 Industrial Ave, Suite 456, City, Country',
}; };
export default en; export default en;