diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..08ca567 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ + +node_modules +npm-debug.log +.next/cache diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1e9db6a --- /dev/null +++ b/Dockerfile @@ -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"] diff --git a/public/product1.png b/public/product1.png new file mode 100644 index 0000000..c8a55cb Binary files /dev/null and b/public/product1.png differ diff --git a/src/app/api/hello/route.ts b/src/app/api/hello/route.ts deleted file mode 100644 index e113783..0000000 --- a/src/app/api/hello/route.ts +++ /dev/null @@ -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 }); -} \ No newline at end of file diff --git a/src/app/api/product_categories/[id]/products/route.ts b/src/app/api/product_categories/[id]/products/route.ts new file mode 100644 index 0000000..508e0d2 --- /dev/null +++ b/src/app/api/product_categories/[id]/products/route.ts @@ -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, + }); +} diff --git a/src/app/api/product_categories/[id]/route.ts b/src/app/api/product_categories/[id]/route.ts index d613ef3..b7a7a76 100644 --- a/src/app/api/product_categories/[id]/route.ts +++ b/src/app/api/product_categories/[id]/route.ts @@ -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, }, }); } diff --git a/src/app/api/product_categories/data.ts b/src/app/api/product_categories/data.ts index 48eca77..bc7c753 100644 --- a/src/app/api/product_categories/data.ts +++ b/src/app/api/product_categories/data.ts @@ -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`; } diff --git a/src/app/api/product_categories/route.ts b/src/app/api/product_categories/route.ts index 812dd81..7ce6d5f 100644 --- a/src/app/api/product_categories/route.ts +++ b/src/app/api/product_categories/route.ts @@ -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, }; }, ); diff --git a/src/app/globals.css b/src/app/globals.css index c35f930..11274b0 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -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; + } +} diff --git a/src/app/product-categories/[product_category_id]/page.tsx b/src/app/product-categories/[product_category_id]/page.tsx index 06446cd..714b15d 100644 --- a/src/app/product-categories/[product_category_id]/page.tsx +++ b/src/app/product-categories/[product_category_id]/page.tsx @@ -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; }) { - 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 (
-
-
- {products.map((product, index) => ( - - ))} +
+ + +
+

+ {productCategory.title} Products +

+
+ {products.map((product, index) => ( + + ))} +
diff --git a/src/app/product-categories/page.tsx b/src/app/product-categories/page.tsx index f852052..0da17e4 100644 --- a/src/app/product-categories/page.tsx +++ b/src/app/product-categories/page.tsx @@ -12,11 +12,11 @@ export default async function ProductsPage() { return (
-
-
+
+
{productCategories.map((category) => ( ))} diff --git a/src/assets/constants/routeFactory/index.ts b/src/assets/constants/routeFactory/index.ts index 82a4f48..b1dd9e2 100644 --- a/src/assets/constants/routeFactory/index.ts +++ b/src/assets/constants/routeFactory/index.ts @@ -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', }, }; diff --git a/src/components/layout/achievementTextBox/achievementTextBox.d.ts b/src/components/layout/achievementTextBox/achievementTextBox.d.ts index 60aa743..8a88eb0 100644 --- a/src/components/layout/achievementTextBox/achievementTextBox.d.ts +++ b/src/components/layout/achievementTextBox/achievementTextBox.d.ts @@ -1,3 +1,4 @@ export interface IAachievementTextBoxProps { title: string; + reverseColor?: boolean; } diff --git a/src/components/layout/achievementTextBox/index.tsx b/src/components/layout/achievementTextBox/index.tsx index 97a1558..1a0f07f 100644 --- a/src/components/layout/achievementTextBox/index.tsx +++ b/src/components/layout/achievementTextBox/index.tsx @@ -3,11 +3,19 @@ import { IAachievementTextBoxProps } from './achievementTextBox'; export default function AchievementTextBox({ title, + reverseColor, }: IAachievementTextBoxProps) { return (
- - {title} + + + {title} +
); } diff --git a/src/components/layout/breadcrumb/breadcrumb.d.ts b/src/components/layout/breadcrumb/breadcrumb.d.ts new file mode 100644 index 0000000..35fdbdb --- /dev/null +++ b/src/components/layout/breadcrumb/breadcrumb.d.ts @@ -0,0 +1,8 @@ +export interface IBreadcrumbProps { + items: IBreadcrumbItem[]; +} + +export interface IBreadcrumbItem { + title: string; + href?: string; +} diff --git a/src/components/layout/breadcrumb/index.tsx b/src/components/layout/breadcrumb/index.tsx new file mode 100644 index 0000000..a5acc51 --- /dev/null +++ b/src/components/layout/breadcrumb/index.tsx @@ -0,0 +1,30 @@ +import Link from 'next/link'; +import { IBreadcrumbProps } from './breadcrumb'; + +export default function Breadcrumb({ items }: IBreadcrumbProps) { + return ( + + ); +} diff --git a/src/components/layout/languageSwitcher.tsx b/src/components/layout/languageSwitcher.tsx deleted file mode 100644 index b67f6f9..0000000 --- a/src/components/layout/languageSwitcher.tsx +++ /dev/null @@ -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 ( - <> - - - - ); -} diff --git a/src/components/layout/navbar/index.tsx b/src/components/layout/navbar/index.tsx index 2ddf6f0..0c082f6 100644 --- a/src/components/layout/navbar/index.tsx +++ b/src/components/layout/navbar/index.tsx @@ -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() {
    - {links.map(({ href, label }) => ( -
  • + {links.map(({ route, title }) => ( +
  • - {label} + {title}
  • ))} diff --git a/src/components/pages/about/TopInfo.tsx b/src/components/pages/about/TopInfo.tsx index e946e00..17f0290 100644 --- a/src/components/pages/about/TopInfo.tsx +++ b/src/components/pages/about/TopInfo.tsx @@ -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() { ))}
-
diff --git a/src/components/pages/home/Products.tsx b/src/components/pages/home/Products.tsx index 49e90ef..fed9350 100644 --- a/src/components/pages/home/Products.tsx +++ b/src/components/pages/home/Products.tsx @@ -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 (
@@ -44,17 +32,19 @@ export default function HomePageProducts() {
{productCategories.map((category, index) => ( -
-

- {category.title} -

-

- {`Includes ${category.count} Products`} -

-
+ +
+

+ {category.title} +

+

+ {`Includes ${category.typesCount} Products`} +

+
+ ))}
diff --git a/src/components/pages/productCategories/ProductCategoryCard.tsx b/src/components/pages/productCategories/ProductCategoryCard.tsx index 67477f1..1c304b8 100644 --- a/src/components/pages/productCategories/ProductCategoryCard.tsx +++ b/src/components/pages/productCategories/ProductCategoryCard.tsx @@ -14,7 +14,7 @@ export default function ProductsCategoryCard({ id, }: IProductCategoryCardProps) { return ( -
+
diff --git a/src/components/pages/productCategories/ProductCategoryCardInProducts.tsx b/src/components/pages/productCategories/ProductCategoryCardInProducts.tsx index eaf3eac..ab25f98 100644 --- a/src/components/pages/productCategories/ProductCategoryCardInProducts.tsx +++ b/src/components/pages/productCategories/ProductCategoryCardInProducts.tsx @@ -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 ( -
+
+
+
+ +
+
+

+ { + t('com_products_products_category_title', { + title: productCategory.title, + }) as string + } +

+ + + +
+

+ {productCategory.description} +

+
+

+ {productCategory.description} +

+
); } diff --git a/src/components/pages/productCategories/productCategoryCard.d.ts b/src/components/pages/productCategories/productCategoryCard.d.ts index aa684a6..9c9d1d8 100644 --- a/src/components/pages/productCategories/productCategoryCard.d.ts +++ b/src/components/pages/productCategories/productCategoryCard.d.ts @@ -7,4 +7,5 @@ export interface IProductCategoryCardProps { typesCount: number; priceTypeTitle: string; bestForTitle: string; + description: string; } diff --git a/src/components/uikit/button/index.tsx b/src/components/uikit/button/index.tsx index 872ca2c..0b762b1 100644 --- a/src/components/uikit/button/index.tsx +++ b/src/components/uikit/button/index.tsx @@ -13,7 +13,7 @@ const Button: React.FC = ({ 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 ? ( diff --git a/src/components/uikit/icons/achievementIconWhite.tsx b/src/components/uikit/icons/achievementIconWhite.tsx new file mode 100644 index 0000000..0a3ee2d --- /dev/null +++ b/src/components/uikit/icons/achievementIconWhite.tsx @@ -0,0 +1,23 @@ +export default function AchievementIconWhite( + props: React.SVGProps, +) { + return ( + + + + + ); +} diff --git a/src/components/uikit/icons/index.tsx b/src/components/uikit/icons/index.tsx index 6565ad9..c5fe0ee 100644 --- a/src/components/uikit/icons/index.tsx +++ b/src/components/uikit/icons/index.tsx @@ -10,12 +10,14 @@ import CategoryGhazaghiIcon from './categoryGhazaghiIcon'; import CategoryGhermezIcon from './categoryGhermezIcon'; import categorySemiromIcon from './categorySemiromIcon'; import CategoryZardIcon from './categoryZardIcon'; +import AchievementIconWhite from './achievementIconWhite'; export const icons = { close: CloseIcon, menu: MenuIcon, setting: SettingIcon, achievement: AchievementIcon, + achievementWhite: AchievementIconWhite, phone: PhoneIcon, instagram: InstagramIcon, showMore: ShowMoreIcon, diff --git a/src/locales/en.ts b/src/locales/en.ts index 92c55d1..4e71440 100644 --- a/src/locales/en.ts +++ b/src/locales/en.ts @@ -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; diff --git a/src/services/products.ts b/src/services/products.ts index 0194442..4fb96f8 100644 --- a/src/services/products.ts +++ b/src/services/products.ts @@ -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 { 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 { + return api + .get(`/product_categories/${categoryId}/products`) + .then(({ data }) => { + return Promise.resolve(data.data); + }) + .catch((error) => Promise.reject(error)); +}