Merge remote-tracking branch 'origin/main' into navbar
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
|
||||
node_modules
|
||||
npm-debug.log
|
||||
.next/cache
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
# Use official Node.js image as the base
|
||||
FROM node:20-alpine AS builder
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /app
|
||||
|
||||
# Install dependencies
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm ci
|
||||
|
||||
# Copy the rest of the app
|
||||
COPY . .
|
||||
|
||||
# Build the Next.js app
|
||||
RUN npm run build
|
||||
|
||||
|
||||
# Set environment variables (optional)
|
||||
ENV NODE_ENV=production
|
||||
|
||||
# Expose port
|
||||
EXPOSE 5000
|
||||
|
||||
# Start the Next.js app
|
||||
CMD ["npm", "start"]
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 360 KiB |
@@ -1,10 +0,0 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
export async function GET(request: Request) {
|
||||
return NextResponse.json({ message: 'Hello from Next.js API Route!' });
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const data = await request.json();
|
||||
return NextResponse.json({ message: 'Received POST request', data });
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { NextResponse, NextRequest } from 'next/server';
|
||||
// import type { NextApiRequestContext } from 'next';
|
||||
|
||||
import { cookies } from 'next/headers';
|
||||
import { productCategories } from '../../data';
|
||||
import images from '@/assets/images/images';
|
||||
|
||||
export async function GET(request: NextRequest, context: any) {
|
||||
const cookieStore = await cookies();
|
||||
const lang = cookieStore.get('lang')?.value || 'en';
|
||||
|
||||
const category = productCategories.find(
|
||||
(cat) => cat.id === context.params.id,
|
||||
);
|
||||
|
||||
if (!category) {
|
||||
return NextResponse.json({ error: 'Not found' }, { status: 404 });
|
||||
}
|
||||
const product = {
|
||||
title: 'Product title',
|
||||
size: 'W 18 * L10',
|
||||
imageSrc: '/product1.png',
|
||||
};
|
||||
const products = Array.from({ length: 10 }, () => product);
|
||||
|
||||
return NextResponse.json({
|
||||
data: products,
|
||||
});
|
||||
}
|
||||
@@ -1,15 +1,16 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { NextResponse, NextRequest } from 'next/server';
|
||||
// import type { NextApiRequestContext } from 'next';
|
||||
|
||||
import { cookies } from 'next/headers';
|
||||
import { productCategories } from '../data';
|
||||
|
||||
export async function GET(
|
||||
request: Request,
|
||||
{ params }: { params: { id: string } },
|
||||
) {
|
||||
export async function GET(request: NextRequest, context: any) {
|
||||
const cookieStore = await cookies();
|
||||
const lang = cookieStore.get('lang')?.value || 'en';
|
||||
|
||||
const category = productCategories.find((cat) => cat.id === params.id);
|
||||
const category = productCategories.find(
|
||||
(cat) => cat.id === context.params.id,
|
||||
);
|
||||
|
||||
if (!category) {
|
||||
return NextResponse.json({ error: 'Not found' }, { status: 404 });
|
||||
@@ -25,6 +26,10 @@ export async function GET(
|
||||
lang === 'fa' && category.fa_bestForTitle
|
||||
? category.fa_bestForTitle
|
||||
: category.bestForTitle;
|
||||
const descriptionData =
|
||||
lang === 'fa' && category.fa_description
|
||||
? category.fa_description
|
||||
: category.description;
|
||||
|
||||
return NextResponse.json({
|
||||
data: {
|
||||
@@ -34,6 +39,7 @@ export async function GET(
|
||||
title: titleData,
|
||||
priceTypeTitle: priceTypeTitleData,
|
||||
bestForTitle: bestForTitleData,
|
||||
description: descriptionData,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -9,6 +9,18 @@ export const productCategories = [
|
||||
fa_priceTypeTitle: 'اقتصادی',
|
||||
bestForTitle: 'Best For Interior Part',
|
||||
fa_bestForTitle: 'Best For Interior Part',
|
||||
fa_description: `Facing Bricks: Characteristics and Features
|
||||
Facing bricks, also known as face bricks or veneer bricks, are specially designed for visible areas of a building, primarily the exterior facades, but also for interior walls where aesthetics are important. Unlike common bricks which are often covered by plaster or other materials, facing bricks are chosen for their visual appeal and ability to withstand environmental conditions.
|
||||
Facing Bricks: Characteristics and Features
|
||||
Facing bricks, also known as face bricks or veneer bricks, are specially designed for visible areas of a building, primarily the exterior facades, but also for interior walls where aesthetics are important. Unlike common bricks which are often covered by plaster or other materials, facing bricks are chosen for their visual appeal and ability to withstand environmental conditions.
|
||||
|
||||
Facing Bricks: Characteristics and Features`,
|
||||
description: `Facing Bricks: Characteristics and Features
|
||||
Facing bricks, also known as face bricks or veneer bricks, are specially designed for visible areas of a building, primarily the exterior facades, but also for interior walls where aesthetics are important. Unlike common bricks which are often covered by plaster or other materials, facing bricks are chosen for their visual appeal and ability to withstand environmental conditions.
|
||||
Facing Bricks: Characteristics and Features
|
||||
Facing bricks, also known as face bricks or veneer bricks, are specially designed for visible areas of a building, primarily the exterior facades, but also for interior walls where aesthetics are important. Unlike common bricks which are often covered by plaster or other materials, facing bricks are chosen for their visual appeal and ability to withstand environmental conditions.
|
||||
|
||||
Facing Bricks: Characteristics and Features`,
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
@@ -20,6 +32,18 @@ export const productCategories = [
|
||||
fa_priceTypeTitle: 'لاکچری',
|
||||
bestForTitle: 'Best For Exterior Part',
|
||||
fa_bestForTitle: 'Best For Exterior Part',
|
||||
fa_description: `Facing Bricks: Characteristics and Features
|
||||
Facing bricks, also known as face bricks or veneer bricks, are specially designed for visible areas of a building, primarily the exterior facades, but also for interior walls where aesthetics are important. Unlike common bricks which are often covered by plaster or other materials, facing bricks are chosen for their visual appeal and ability to withstand environmental conditions.
|
||||
Facing Bricks: Characteristics and Features
|
||||
Facing bricks, also known as face bricks or veneer bricks, are specially designed for visible areas of a building, primarily the exterior facades, but also for interior walls where aesthetics are important. Unlike common bricks which are often covered by plaster or other materials, facing bricks are chosen for their visual appeal and ability to withstand environmental conditions.
|
||||
|
||||
Facing Bricks: Characteristics and Features`,
|
||||
description: `Facing Bricks: Characteristics and Features
|
||||
Facing bricks, also known as face bricks or veneer bricks, are specially designed for visible areas of a building, primarily the exterior facades, but also for interior walls where aesthetics are important. Unlike common bricks which are often covered by plaster or other materials, facing bricks are chosen for their visual appeal and ability to withstand environmental conditions.
|
||||
Facing Bricks: Characteristics and Features
|
||||
Facing bricks, also known as face bricks or veneer bricks, are specially designed for visible areas of a building, primarily the exterior facades, but also for interior walls where aesthetics are important. Unlike common bricks which are often covered by plaster or other materials, facing bricks are chosen for their visual appeal and ability to withstand environmental conditions.
|
||||
|
||||
Facing Bricks: Characteristics and Features`,
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
@@ -31,6 +55,18 @@ export const productCategories = [
|
||||
fa_priceTypeTitle: 'اقتصادی',
|
||||
bestForTitle: 'Best For Facing',
|
||||
fa_bestForTitle: 'Best For Facing',
|
||||
fa_description: `Facing Bricks: Characteristics and Features
|
||||
Facing bricks, also known as face bricks or veneer bricks, are specially designed for visible areas of a building, primarily the exterior facades, but also for interior walls where aesthetics are important. Unlike common bricks which are often covered by plaster or other materials, facing bricks are chosen for their visual appeal and ability to withstand environmental conditions.
|
||||
Facing Bricks: Characteristics and Features
|
||||
Facing bricks, also known as face bricks or veneer bricks, are specially designed for visible areas of a building, primarily the exterior facades, but also for interior walls where aesthetics are important. Unlike common bricks which are often covered by plaster or other materials, facing bricks are chosen for their visual appeal and ability to withstand environmental conditions.
|
||||
|
||||
Facing Bricks: Characteristics and Features`,
|
||||
description: `Facing Bricks: Characteristics and Features
|
||||
Facing bricks, also known as face bricks or veneer bricks, are specially designed for visible areas of a building, primarily the exterior facades, but also for interior walls where aesthetics are important. Unlike common bricks which are often covered by plaster or other materials, facing bricks are chosen for their visual appeal and ability to withstand environmental conditions.
|
||||
Facing Bricks: Characteristics and Features
|
||||
Facing bricks, also known as face bricks or veneer bricks, are specially designed for visible areas of a building, primarily the exterior facades, but also for interior walls where aesthetics are important. Unlike common bricks which are often covered by plaster or other materials, facing bricks are chosen for their visual appeal and ability to withstand environmental conditions.
|
||||
|
||||
Facing Bricks: Characteristics and Features`,
|
||||
},
|
||||
{
|
||||
id: '40',
|
||||
@@ -42,6 +78,18 @@ export const productCategories = [
|
||||
fa_priceTypeTitle: 'اقتصادی',
|
||||
bestForTitle: 'Best For Building',
|
||||
fa_bestForTitle: 'Best For Building',
|
||||
fa_description: `Facing Bricks: Characteristics and Features
|
||||
Facing bricks, also known as face bricks or veneer bricks, are specially designed for visible areas of a building, primarily the exterior facades, but also for interior walls where aesthetics are important. Unlike common bricks which are often covered by plaster or other materials, facing bricks are chosen for their visual appeal and ability to withstand environmental conditions.
|
||||
Facing Bricks: Characteristics and Features
|
||||
Facing bricks, also known as face bricks or veneer bricks, are specially designed for visible areas of a building, primarily the exterior facades, but also for interior walls where aesthetics are important. Unlike common bricks which are often covered by plaster or other materials, facing bricks are chosen for their visual appeal and ability to withstand environmental conditions.
|
||||
|
||||
Facing Bricks: Characteristics and Features`,
|
||||
description: `Facing Bricks: Characteristics and Features
|
||||
Facing bricks, also known as face bricks or veneer bricks, are specially designed for visible areas of a building, primarily the exterior facades, but also for interior walls where aesthetics are important. Unlike common bricks which are often covered by plaster or other materials, facing bricks are chosen for their visual appeal and ability to withstand environmental conditions.
|
||||
Facing Bricks: Characteristics and Features
|
||||
Facing bricks, also known as face bricks or veneer bricks, are specially designed for visible areas of a building, primarily the exterior facades, but also for interior walls where aesthetics are important. Unlike common bricks which are often covered by plaster or other materials, facing bricks are chosen for their visual appeal and ability to withstand environmental conditions.
|
||||
|
||||
Facing Bricks: Characteristics and Features`,
|
||||
},
|
||||
] as APIProductCategoryEntity[];
|
||||
|
||||
@@ -55,4 +103,16 @@ interface APIProductCategoryEntity {
|
||||
fa_priceTypeTitle?: string;
|
||||
bestForTitle: string;
|
||||
fa_bestForTitle?: string;
|
||||
fa_description?: `Facing Bricks: Characteristics and Features
|
||||
Facing bricks, also known as face bricks or veneer bricks, are specially designed for visible areas of a building, primarily the exterior facades, but also for interior walls where aesthetics are important. Unlike common bricks which are often covered by plaster or other materials, facing bricks are chosen for their visual appeal and ability to withstand environmental conditions.
|
||||
Facing Bricks: Characteristics and Features
|
||||
Facing bricks, also known as face bricks or veneer bricks, are specially designed for visible areas of a building, primarily the exterior facades, but also for interior walls where aesthetics are important. Unlike common bricks which are often covered by plaster or other materials, facing bricks are chosen for their visual appeal and ability to withstand environmental conditions.
|
||||
|
||||
Facing Bricks: Characteristics and Features`;
|
||||
description?: `Facing Bricks: Characteristics and Features
|
||||
Facing bricks, also known as face bricks or veneer bricks, are specially designed for visible areas of a building, primarily the exterior facades, but also for interior walls where aesthetics are important. Unlike common bricks which are often covered by plaster or other materials, facing bricks are chosen for their visual appeal and ability to withstand environmental conditions.
|
||||
Facing Bricks: Characteristics and Features
|
||||
Facing bricks, also known as face bricks or veneer bricks, are specially designed for visible areas of a building, primarily the exterior facades, but also for interior walls where aesthetics are important. Unlike common bricks which are often covered by plaster or other materials, facing bricks are chosen for their visual appeal and ability to withstand environmental conditions.
|
||||
|
||||
Facing Bricks: Characteristics and Features`;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { productCategories } from './data';
|
||||
export async function GET(request: Request) {
|
||||
let data = productCategories;
|
||||
|
||||
const cookieStore = cookies();
|
||||
const cookieStore = await cookies();
|
||||
const lang = (await cookieStore).get('lang')?.value || 'fa';
|
||||
|
||||
if (lang === 'fa') {
|
||||
@@ -20,6 +20,8 @@ export async function GET(request: Request) {
|
||||
fa_priceTypeTitle,
|
||||
bestForTitle,
|
||||
fa_bestForTitle,
|
||||
description,
|
||||
fa_description,
|
||||
}) => {
|
||||
const titleData = lang === 'fa' && fa_title ? fa_title : title;
|
||||
const priceTypeTitleData =
|
||||
@@ -28,6 +30,8 @@ export async function GET(request: Request) {
|
||||
: priceTypeTitle;
|
||||
const bestForTitleData =
|
||||
lang === 'fa' && fa_bestForTitle ? fa_bestForTitle : bestForTitle;
|
||||
const descriptionData =
|
||||
lang === 'fa' && fa_description ? fa_description : description;
|
||||
return {
|
||||
id,
|
||||
icon,
|
||||
@@ -35,6 +39,7 @@ export async function GET(request: Request) {
|
||||
title: titleData,
|
||||
priceTypeTitle: priceTypeTitleData,
|
||||
bestForTitle: bestForTitleData,
|
||||
description: descriptionData,
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
+40
-1
@@ -106,10 +106,19 @@
|
||||
}
|
||||
|
||||
* {
|
||||
|
||||
font-family: 'Manrope';
|
||||
}
|
||||
|
||||
html{
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
@media (width <=48rem) {
|
||||
html {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
:root {
|
||||
--primary: #FA003F;
|
||||
@@ -185,3 +194,33 @@
|
||||
padding-inline: 8rem;
|
||||
}
|
||||
}
|
||||
|
||||
.container-fluid {
|
||||
width: 100%;
|
||||
padding-inline: 1rem;
|
||||
|
||||
@media (width >=40rem) {
|
||||
max-width: 100%;
|
||||
padding-inline: 1rem;
|
||||
}
|
||||
|
||||
@media (width >=48rem) {
|
||||
max-width: 100%;
|
||||
padding-inline: 1.5rem;
|
||||
}
|
||||
|
||||
@media (width >=64rem) {
|
||||
max-width: 100%;
|
||||
padding-inline: 2rem;
|
||||
}
|
||||
|
||||
@media (width >=80rem) {
|
||||
max-width: 100%;
|
||||
padding-inline: 3rem;
|
||||
}
|
||||
|
||||
@media (width >=96rem) {
|
||||
max-width: 100%;
|
||||
padding-inline: 5rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +1,55 @@
|
||||
import routeFactory from '@/assets/constants/routeFactory';
|
||||
import images from '@/assets/images/images';
|
||||
import Breadcrumb from '@/components/layout/breadcrumb';
|
||||
import { IBreadcrumbItem } from '@/components/layout/breadcrumb/breadcrumb';
|
||||
import InnerPageBanner from '@/components/layout/innerPages/banner';
|
||||
import ProductCategoryCardInProducts from '@/components/pages/productCategories/ProductCategoryCardInProducts';
|
||||
import ProductCard from '@/components/pages/products/ProductCard';
|
||||
import { getProductCategoryById } from '@/services/products';
|
||||
import { getProductCategoryById, getProducts } from '@/services/products';
|
||||
|
||||
interface IParams {
|
||||
product_category_id: string;
|
||||
}
|
||||
|
||||
export default async function ProductPage({
|
||||
params,
|
||||
}: {
|
||||
params: { product_category_id: string };
|
||||
params: Promise<IParams>;
|
||||
}) {
|
||||
const { product_category_id: productCategoryId } = params;
|
||||
const { product_category_id: productCategoryId } = await params;
|
||||
|
||||
const productCategory = await getProductCategoryById(productCategoryId);
|
||||
const products = await getProducts(productCategoryId);
|
||||
|
||||
const breadcrumbItems = [
|
||||
{
|
||||
...routeFactory.productCategories,
|
||||
href: routeFactory.productCategories.route(),
|
||||
},
|
||||
{ title: productCategory.title },
|
||||
] as IBreadcrumbItem[];
|
||||
|
||||
const product = {
|
||||
title: 'Product title',
|
||||
size: 'W 18 * L10',
|
||||
imageSrc: images.product1.src,
|
||||
};
|
||||
const products = Array.from({ length: 10 }, () => product);
|
||||
return (
|
||||
<main className="">
|
||||
<InnerPageBanner
|
||||
title={productCategory.title}
|
||||
imageSrc={images.homeProductBack.src}
|
||||
/>
|
||||
<div className="container mx-auto my-24">
|
||||
<div className="grid grid-cols-4 gap-3">
|
||||
{products.map((product, index) => (
|
||||
<ProductCard {...product} key={index} />
|
||||
))}
|
||||
<div className="container-fluid mx-auto my-24">
|
||||
<Breadcrumb items={breadcrumbItems} />
|
||||
<ProductCategoryCardInProducts
|
||||
productCategory={productCategory}
|
||||
className="mt-6"
|
||||
/>
|
||||
<div className="mt-20">
|
||||
<h2 className="mb-6 text-3xl font-semibold text-gray-500">
|
||||
{productCategory.title} Products
|
||||
</h2>
|
||||
<div className="grid gap-3 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5">
|
||||
{products.map((product, index) => (
|
||||
<ProductCard {...product} key={index} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@@ -12,11 +12,11 @@ export default async function ProductsPage() {
|
||||
return (
|
||||
<main>
|
||||
<InnerPageBanner
|
||||
title={t('pages_projects_title') as string}
|
||||
title={t('pages_product_categories_title') as string}
|
||||
imageSrc={images.aboutBanner.src}
|
||||
/>
|
||||
<div className="container mx-auto my-24">
|
||||
<div className="grid w-full grid-cols-4 gap-4">
|
||||
<div className="container-fluid mx-auto my-24">
|
||||
<div className="grid w-full grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4">
|
||||
{productCategories.map((category) => (
|
||||
<ProductsCategoryCard key={category.id} {...category} />
|
||||
))}
|
||||
|
||||
@@ -2,31 +2,31 @@ import { t, translates } from '@/locales/translates';
|
||||
|
||||
export default {
|
||||
home: {
|
||||
title: t('pages_home_title'),
|
||||
title: t('pages_home_title') as string,
|
||||
route: () => '/',
|
||||
},
|
||||
productCategories: {
|
||||
title: t('pages_product_categories_title'),
|
||||
title: t('pages_product_categories_title') as string,
|
||||
route: () => '/product-categories',
|
||||
},
|
||||
products: {
|
||||
title: t('pages_products_title'),
|
||||
title: t('pages_products_title') as string,
|
||||
route: (productId: string) => `/product-categories/${productId}`,
|
||||
},
|
||||
projects: {
|
||||
title: t('pages_projects_title'),
|
||||
title: t('pages_projects_title') as string,
|
||||
route: () => '/projects',
|
||||
},
|
||||
about: {
|
||||
title: t('pages_about_title'),
|
||||
title: t('pages_about_title') as string,
|
||||
route: () => '/about-us',
|
||||
},
|
||||
blog: {
|
||||
title: t('pages_blog_title'),
|
||||
blog: {
|
||||
title: t('pages_blog_title') as string,
|
||||
route: () => '/blog-us',
|
||||
},
|
||||
contact: {
|
||||
title: t('pages_contact_title'),
|
||||
title: t('pages_contact_title') as string,
|
||||
route: () => '/contact',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
))}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -72,6 +72,9 @@ Perforated/Hollow Bricks: These bricks have holes, offering several advantages:
|
||||
Solid Bricks: These bricks are without holes and possess very high compressive strength. They are ideal for load-bearing structures and walls requiring maximum strength.
|
||||
2. Classification by Raw Material Type and Composition: From Clay to Concrete
|
||||
The material from which a brick is made determines its physical and chemical properties.`,
|
||||
|
||||
com_products_products_category_title: ({ title }: { title: string }) =>
|
||||
`Products of ${title} Category`,
|
||||
};
|
||||
|
||||
export default en;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { IProductCategoryCardProps } from '@/components/pages/productCategories/productCategoryCard.d';
|
||||
import type { IProductCardProps } from '@/components/pages/products/productCard.d';
|
||||
import api from '@/lib/axios';
|
||||
|
||||
export function getProductCategories() {
|
||||
@@ -9,7 +11,9 @@ export function getProductCategories() {
|
||||
.catch((error) => Promise.reject(error));
|
||||
}
|
||||
|
||||
export function getProductCategoryById(categoryId: string) {
|
||||
export function getProductCategoryById(
|
||||
categoryId: string,
|
||||
): Promise<IProductCategoryCardProps> {
|
||||
return api
|
||||
.get(`/product_categories/${categoryId}`)
|
||||
.then(({ data }) => {
|
||||
@@ -17,3 +21,12 @@ export function getProductCategoryById(categoryId: string) {
|
||||
})
|
||||
.catch((error) => Promise.reject(error));
|
||||
}
|
||||
|
||||
export function getProducts(categoryId: string): Promise<IProductCardProps[]> {
|
||||
return api
|
||||
.get(`/product_categories/${categoryId}/products`)
|
||||
.then(({ data }) => {
|
||||
return Promise.resolve(data.data);
|
||||
})
|
||||
.catch((error) => Promise.reject(error));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user