Merge remote-tracking branch 'origin/main' into navbar

This commit is contained in:
zahravaziri
2025-06-05 17:01:17 +03:30
28 changed files with 404 additions and 113 deletions
@@ -1,3 +1,4 @@
export interface IAachievementTextBoxProps {
title: string;
reverseColor?: boolean;
}
@@ -3,11 +3,19 @@ import { IAachievementTextBoxProps } from './achievementTextBox';
export default function AchievementTextBox({
title,
reverseColor,
}: IAachievementTextBoxProps) {
return (
<div className="flex items-center gap-3">
<Icon name="achievement" className="text-primary" />
<span className="text-base text-gray-500">{title}</span>
<Icon
name={reverseColor ? 'achievementWhite' : 'achievement'}
className={`${reverseColor ? 'text-white' : 'text-primary'}`}
/>
<span
className={`text-base ${reverseColor ? 'text-white' : 'text-gray-500'}`}
>
{title}
</span>
</div>
);
}
+8
View File
@@ -0,0 +1,8 @@
export interface IBreadcrumbProps {
items: IBreadcrumbItem[];
}
export interface IBreadcrumbItem {
title: string;
href?: string;
}
@@ -0,0 +1,30 @@
import Link from 'next/link';
import { IBreadcrumbProps } from './breadcrumb';
export default function Breadcrumb({ items }: IBreadcrumbProps) {
return (
<nav aria-label="breadcrumb">
<div className="flex items-center gap-2">
{items.map((item, index) => (
<div key={index} className="flex items-center gap-2">
{index > 0 && (
<span className="text-sm font-normal text-gray-300"></span>
)}
{item.href ? (
<Link
href={item.href}
className="text-base font-normal text-gray-300"
>
{item.title}
</Link>
) : (
<span className="text-primary text-base font-bold">
{item.title}
</span>
)}
</div>
))}
</div>
</nav>
);
}
@@ -1,17 +0,0 @@
'use client';
import { changeLanguage, TLocales } from '@/locales/translates';
import Button from '../Button';
export default function ChangeLangButton() {
const changeLang = (lang: TLocales) => {
changeLanguage(lang);
window.location.reload(); // Reload to get new SSR content
};
return (
<>
<Button onClick={() => changeLang('en')}>English</Button>
<Button onClick={() => changeLang('fa')}>فارسی</Button>
</>
);
}
+12 -11
View File
@@ -94,18 +94,19 @@ import { usePathname } from 'next/navigation';
import { useState } from 'react';
import HamburgerMenu from '../hamburgerMenu';
import { t } from '@/locales/translates';
import routeFactory from '@/assets/constants/routeFactory';
export default function Navbar() {
const [open, setOpen] = useState(false);
const pathname = usePathname();
const links = [
{ href: '/', label: t('pages_home_title') as string },
{ href: '/about', label: t('pages_about_title') as string },
{ href: '/blog', label: t('pages_blog_title') as string },
{ href: '/contact', label: t('pages_contact_title') as string },
{ href: '/product', label: t('pages_product_categories_title') as string },
{ href: '/project', label: t('pages_projects_title') as string },
routeFactory.home,
routeFactory.productCategories,
routeFactory.projects,
routeFactory.blog,
routeFactory.about,
routeFactory.contact,
];
const linkClass = (href: string) =>
pathname === href ? 'text-primary font-semibold' : 'text-white';
@@ -123,13 +124,13 @@ export default function Navbar() {
</Link>
<ul className="hidden space-x-6 text-sm md:flex lg:text-base">
{links.map(({ href, label }) => (
<li key={href}>
{links.map(({ route, title }) => (
<li key={route()}>
<Link
href={href}
className={`${linkClass(href)} hover:text-primary-light`}
href={route()}
className={`${linkClass(route())} hover:text-primary-light`}
>
{label}
{title}
</Link>
</li>
))}
+2 -1
View File
@@ -1,3 +1,4 @@
import routeFactory from '@/assets/constants/routeFactory';
import images from '@/assets/images/images';
import AchievementTextBox from '@/components/layout/achievementTextBox';
import SectionTitle from '@/components/layout/sectionTitle';
@@ -60,7 +61,7 @@ export default function AboutTopInfo() {
))}
</div>
<div className="mt-10">
<Button endIcon="showMore">
<Button endIcon="showMore" link={routeFactory.contact.route()}>
{t('pages_contact_title') as string}
</Button>
</div>
+20 -30
View File
@@ -1,26 +1,14 @@
import images from '@/assets/images/images';
import SectionTitle from '@/components/layout/sectionTitle';
import { t } from '@/locales/translates';
import type { IProductCategoryCardProps } from '../productCategories/productCategoryCard.d';
import { getProductCategories } from '@/services/products';
import Link from 'next/link';
import routeFactory from '@/assets/constants/routeFactory';
export default function HomePageProducts() {
const productCategories = [
{
title: 'Brick Categories',
count: 30,
},
{
title: 'Brick Categories',
count: 30,
},
{
title: 'Brick Categories',
count: 30,
},
{
title: 'Brick Categories',
count: 30,
},
];
export default async function HomePageProducts() {
const productCategories: IProductCategoryCardProps[] =
await getProductCategories();
return (
<div className="w-full">
@@ -44,17 +32,19 @@ export default function HomePageProducts() {
<div className="relative top-[-100px] z-20 container mx-auto w-full">
<div className="grid grid-cols-1 gap-8 rounded-4xl bg-gray-50 py-10 shadow-2xl md:grid-cols-2 lg:grid-cols-4">
{productCategories.map((category, index) => (
<div
key={index}
className={`flex flex-col items-center justify-center gap-4 p-6 text-center ${index ? 'border-s-[1px] border-gray-100' : ''}`}
>
<h3 className="text-xl font-bold text-gray-500">
{category.title}
</h3>
<p className="text-lg font-light text-gray-400">
{`Includes ${category.count} Products`}
</p>
</div>
<Link href={routeFactory.products.route(category.id)}>
<div
key={index}
className={`flex flex-col items-center justify-center gap-4 p-6 text-center ${index ? 'border-s-[1px] border-gray-100' : ''}`}
>
<h3 className="text-xl font-bold text-gray-500">
{category.title}
</h3>
<p className="text-lg font-light text-gray-400">
{`Includes ${category.typesCount} Products`}
</p>
</div>
</Link>
))}
</div>
</div>
@@ -14,7 +14,7 @@ export default function ProductsCategoryCard({
id,
}: IProductCategoryCardProps) {
return (
<div className="relative overflow-hidden rounded-4xl bg-gray-100 p-10">
<div className="relative overflow-hidden rounded-4xl bg-gray-100 p-3 sm:p-6 md:p-10">
<div className="absolute -end-2 -top-2">
<Icon name={icon} className="text-primary h-36 w-36" />
</div>
@@ -1,9 +1,58 @@
import AchievementTextBox from '@/components/layout/achievementTextBox';
import type { IProductCategoryCardProps } from './productCategoryCard.d';
import { t } from '@/locales/translates';
import { Icon } from '@/components/uikit/icons';
export default function ProductCategoryCardInProducts(
productCategory: IProductCategoryCardProps,
) {
export default function ProductCategoryCardInProducts({
productCategory,
className,
}: {
productCategory: IProductCategoryCardProps;
className?: string;
}) {
return (
<div className="bg-primary relative overflow-hidden rounded-4xl p-20 pe-40"></div>
<div className="">
<div
className={`bg-primary relative grid overflow-hidden rounded-4xl p-6 max-lg:pt-24 max-lg:pb-12 lg:grid-cols-2 lg:p-20 lg:pe-40 ${className}`}
>
<div className="absolute -end-2 -top-2">
<Icon
name={productCategory.icon}
className="h-24 w-24 text-white md:h-36 md:w-36"
/>
</div>
<div className="flex flex-col gap-5">
<h1 className="text-2xl font-semibold text-white">
{
t('com_products_products_category_title', {
title: productCategory.title,
}) as string
}
</h1>
<AchievementTextBox
reverseColor
title={
t('com_products_category_types_count', {
count: productCategory.typesCount,
}) as string
}
/>
<AchievementTextBox
reverseColor
title={productCategory.priceTypeTitle}
/>
<AchievementTextBox
reverseColor
title={productCategory.bestForTitle}
/>
</div>
<p className="text-xl text-white max-lg:hidden">
{productCategory.description}
</p>
</div>
<p className="mt-5 text-xl text-gray-500 lg:hidden">
{productCategory.description}
</p>
</div>
);
}
@@ -7,4 +7,5 @@ export interface IProductCategoryCardProps {
typesCount: number;
priceTypeTitle: string;
bestForTitle: string;
description: string;
}
+1 -1
View File
@@ -13,7 +13,7 @@ const Button: React.FC<ITextButtonProps> = ({
link,
...props
}) => {
const wrapperClass = `group bg-primary relative h-12 cursor-pointer rounded-[0.5625rem] p-[0.125rem] flex ${className || ''}`;
const wrapperClass = `group bg-primary relative h-12 cursor-pointer rounded-[0.5625rem] p-[0.125rem] flex w-fit ${className || ''}`;
return link ? (
<Link href={link} className={wrapperClass} {...props}>
@@ -0,0 +1,23 @@
export default function AchievementIconWhite(
props: React.SVGProps<SVGSVGElement>,
) {
return (
<svg
{...props}
width="24"
height="25"
viewBox="0 0 24 25"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M23.878 10.9739C23.8379 10.6942 23.6342 10.4657 23.361 10.3939L22.1659 10.0794C21.92 9.11899 21.5402 8.20366 21.033 7.34871L21.6566 6.27949C21.7988 6.03546 21.7813 5.73002 21.612 5.5039C21.2711 5.04837 20.892 4.61116 20.4853 4.20429C20.0795 3.79835 19.6422 3.41923 19.1856 3.07732C18.9595 2.90801 18.654 2.89043 18.4099 3.03288L17.3408 3.65651C16.4859 3.14923 15.5706 2.76949 14.6102 2.52354L14.2958 1.32855C14.2238 1.05532 13.9954 0.851648 13.7157 0.811476C12.5876 0.64943 11.4123 0.64943 10.2842 0.811476C10.0045 0.851601 9.77611 1.05527 9.7042 1.32855L9.38972 2.52354C8.42939 2.76944 7.51416 3.14918 6.65911 3.65651L5.59008 3.03288C5.34605 2.89052 5.04047 2.9081 4.81434 3.07732C4.35783 3.41918 3.92053 3.7983 3.51464 4.20429C3.10795 4.61112 2.72888 5.04832 2.38791 5.50385C2.21864 5.72998 2.20111 6.03541 2.34338 6.27944L2.96691 7.34866C2.45967 8.20371 2.07994 9.11899 1.83403 10.0793L0.639 10.3938C0.365719 10.4657 0.162047 10.6942 0.121969 10.9739C0.0410625 11.5382 0 12.1154 0 12.6896C0 13.2638 0.0410625 13.841 0.122016 14.4053C0.162094 14.685 0.365766 14.9135 0.639047 14.9854L1.83408 15.2999C2.08003 16.2603 2.45977 17.1756 2.96695 18.0305L2.34342 19.0998C2.20116 19.3438 2.21869 19.6492 2.38795 19.8754C2.72892 20.3309 3.108 20.7681 3.51473 21.175C3.92053 21.5809 4.35783 21.96 4.81439 22.3019C5.04052 22.4712 5.346 22.4888 5.59012 22.3464L6.65916 21.7227C7.51411 22.23 8.42944 22.6098 9.38977 22.8557L9.70425 24.0507C9.77616 24.3239 10.0046 24.5276 10.2843 24.5677C10.8483 24.6488 11.4256 24.6899 12 24.6899C12.5744 24.6899 13.1517 24.6488 13.7158 24.5678C13.9955 24.5277 14.2239 24.324 14.2958 24.0507L14.6103 22.8558C15.5706 22.6099 16.4858 22.2301 17.3409 21.7228L18.4099 22.3465C18.654 22.4889 18.9596 22.4713 19.1857 22.302C19.6422 21.9602 20.0795 21.581 20.4854 21.1751C20.892 20.7682 21.2711 20.331 21.6121 19.8755C21.7814 19.6494 21.7989 19.3439 21.6566 19.0999L21.0331 18.0307C21.5403 17.1756 21.9201 16.2604 22.166 15.3L23.361 14.9855C23.6343 14.9137 23.838 14.6851 23.878 14.4054C23.959 13.8411 24 13.2639 24 12.6897C24 12.1156 23.9589 11.5382 23.878 10.9739ZM12 20.1711C7.87481 20.1711 4.51875 16.8151 4.51875 12.6899C4.51875 8.56474 7.87481 5.20868 12 5.20868C16.1252 5.20868 19.4813 8.56474 19.4813 12.6899C19.4813 16.8151 16.1252 20.1711 12 20.1711Z"
fill="#F5F5F5"
/>
<path
d="M12.0008 6.61475C8.651 6.61475 5.92578 9.33997 5.92578 12.6898C5.92578 16.0395 8.651 18.7648 12.0008 18.7648C15.3506 18.7648 18.0758 16.0395 18.0758 12.6898C18.0758 9.33997 15.3506 6.61475 12.0008 6.61475ZM15.5105 11.6807L11.7448 15.4463C11.613 15.5782 11.4342 15.6523 11.2477 15.6523C11.0612 15.6523 10.8823 15.5782 10.7505 15.4463L8.86766 13.5635C8.59306 13.289 8.59306 12.8437 8.86766 12.5691C9.1422 12.2946 9.58742 12.2946 9.86202 12.5691L11.2477 13.9548L14.5161 10.6863C14.7907 10.4118 15.2359 10.4118 15.5105 10.6863C15.785 10.9609 15.785 11.4061 15.5105 11.6807Z"
fill="#F5F5F5"
/>
</svg>
);
}
+3 -2
View File
@@ -10,13 +10,14 @@ import CategoryGhazaghiIcon from './categoryGhazaghiIcon';
import CategoryGhermezIcon from './categoryGhermezIcon';
import categorySemiromIcon from './categorySemiromIcon';
import CategoryZardIcon from './categoryZardIcon';
import { LocationIcon } from './locationIcon';
import { EmailIcon } from './emailIcon';
import AchievementIconWhite from './achievementIconWhite';
export const icons = {
close: CloseIcon,
menu: MenuIcon,
setting: SettingIcon,
achievement: AchievementIcon,
achievementWhite: AchievementIconWhite,
phone: PhoneIcon,
instagram: InstagramIcon,
showMore: ShowMoreIcon,