update project api and page, set pagination in blog and create content generator component, create blog single page, ui fix
This commit is contained in:
@@ -104,6 +104,7 @@ declare const messages: {
|
|||||||
|
|
||||||
"page_about_metadata_title": "About Us",
|
"page_about_metadata_title": "About Us",
|
||||||
"page_about_metadata_description": "Learn more about our commitment to producing high-quality bricks using both traditional and modern manufacturing techniques.",
|
"page_about_metadata_description": "Learn more about our commitment to producing high-quality bricks using both traditional and modern manufacturing techniques.",
|
||||||
|
"page_about_description": "Learn more about our commitment to producing high-quality bricks using both traditional and modern manufacturing techniques. Learn more about our commitment to producing high-quality bricks using both traditional and modern manufacturing techniques. Learn more about our commitment to producing high-quality bricks using both traditional and modern manufacturing techniques. Learn more about our commitment to producing high-quality bricks using both traditional and modern manufacturing techniques.",
|
||||||
|
|
||||||
"page_home_metadata_title": "Home",
|
"page_home_metadata_title": "Home",
|
||||||
"page_home_metadata_description": "Welcome to our official website, where we showcase our expertise, quality, and innovation in brick manufacturing.",
|
"page_home_metadata_description": "Welcome to our official website, where we showcase our expertise, quality, and innovation in brick manufacturing.",
|
||||||
@@ -116,6 +117,7 @@ declare const messages: {
|
|||||||
|
|
||||||
"page_blog_metadata_title": "Blog",
|
"page_blog_metadata_title": "Blog",
|
||||||
"page_blog_metadata_description": "Stay informed with our latest articles, news, and tips related to bricks and building materials.",
|
"page_blog_metadata_description": "Stay informed with our latest articles, news, and tips related to bricks and building materials.",
|
||||||
|
"page_blog_details_CTA": "Show Details",
|
||||||
|
|
||||||
"page_projects_metadata_title": "Projects",
|
"page_projects_metadata_title": "Projects",
|
||||||
"page_projects_metadata_description": "Discover successful projects completed using our high-quality bricks and expert craftsmanship.",
|
"page_projects_metadata_description": "Discover successful projects completed using our high-quality bricks and expert craftsmanship.",
|
||||||
|
|||||||
@@ -101,6 +101,7 @@
|
|||||||
|
|
||||||
"page_about_metadata_title": "About Us",
|
"page_about_metadata_title": "About Us",
|
||||||
"page_about_metadata_description": "Learn more about our commitment to producing high-quality bricks using both traditional and modern manufacturing techniques.",
|
"page_about_metadata_description": "Learn more about our commitment to producing high-quality bricks using both traditional and modern manufacturing techniques.",
|
||||||
|
"page_about_description": "Learn more about our commitment to producing high-quality bricks using both traditional and modern manufacturing techniques. Learn more about our commitment to producing high-quality bricks using both traditional and modern manufacturing techniques. Learn more about our commitment to producing high-quality bricks using both traditional and modern manufacturing techniques. Learn more about our commitment to producing high-quality bricks using both traditional and modern manufacturing techniques.",
|
||||||
|
|
||||||
"page_home_metadata_title": "Home",
|
"page_home_metadata_title": "Home",
|
||||||
"page_home_metadata_description": "Welcome to our official website, where we showcase our expertise, quality, and innovation in brick manufacturing.",
|
"page_home_metadata_description": "Welcome to our official website, where we showcase our expertise, quality, and innovation in brick manufacturing.",
|
||||||
@@ -113,6 +114,7 @@
|
|||||||
|
|
||||||
"page_blog_metadata_title": "Blog",
|
"page_blog_metadata_title": "Blog",
|
||||||
"page_blog_metadata_description": "Stay informed with our latest articles, news, and tips related to bricks and building materials.",
|
"page_blog_metadata_description": "Stay informed with our latest articles, news, and tips related to bricks and building materials.",
|
||||||
|
"page_blog_details_CTA": "Show Details",
|
||||||
|
|
||||||
"page_projects_metadata_title": "Projects",
|
"page_projects_metadata_title": "Projects",
|
||||||
"page_projects_metadata_description": "Discover successful projects completed using our high-quality bricks and expert craftsmanship.",
|
"page_projects_metadata_description": "Discover successful projects completed using our high-quality bricks and expert craftsmanship.",
|
||||||
|
|||||||
@@ -98,6 +98,7 @@
|
|||||||
|
|
||||||
"page_about_metadata_title": "درباره ما",
|
"page_about_metadata_title": "درباره ما",
|
||||||
"page_about_metadata_description": "با ما بیشتر آشنا شوید؛ متعهد به تولید آجرهای باکیفیت با بهرهگیری از فناوریهای نوین و روشهای ساخت پیشرفته.",
|
"page_about_metadata_description": "با ما بیشتر آشنا شوید؛ متعهد به تولید آجرهای باکیفیت با بهرهگیری از فناوریهای نوین و روشهای ساخت پیشرفته.",
|
||||||
|
"page_about_description": "با ما بیشتر آشنا شوید؛ متعهد به تولید آجرهای باکیفیت با بهرهگیری از فناوریهای نوین و روشهای ساخت پیشرفته. با ما بیشتر آشنا شوید؛ متعهد به تولید آجرهای باکیفیت با بهرهگیری از فناوریهای نوین و روشهای ساخت پیشرفته. با ما بیشتر آشنا شوید؛ متعهد به تولید آجرهای باکیفیت با بهرهگیری از فناوریهای نوین و روشهای ساخت پیشرفته. با ما بیشتر آشنا شوید؛ متعهد به تولید آجرهای باکیفیت با بهرهگیری از فناوریهای نوین و روشهای ساخت پیشرفته.",
|
||||||
|
|
||||||
"page_home_metadata_title": "خانه",
|
"page_home_metadata_title": "خانه",
|
||||||
"page_home_metadata_description": "به وبسایت رسمی ما خوش آمدید؛ جایی که کیفیت، تجربه و نوآوری در تولید انواع آجر به نمایش گذاشته میشود.",
|
"page_home_metadata_description": "به وبسایت رسمی ما خوش آمدید؛ جایی که کیفیت، تجربه و نوآوری در تولید انواع آجر به نمایش گذاشته میشود.",
|
||||||
@@ -110,6 +111,7 @@
|
|||||||
|
|
||||||
"page_blog_metadata_title": "بلاگ",
|
"page_blog_metadata_title": "بلاگ",
|
||||||
"page_blog_metadata_description": "با مقالات، اخبار و آموزشهای ما در حوزه آجر و مصالح ساختمانی بهروز بمانید.",
|
"page_blog_metadata_description": "با مقالات، اخبار و آموزشهای ما در حوزه آجر و مصالح ساختمانی بهروز بمانید.",
|
||||||
|
"page_blog_details_CTA": "جزییات مقاله",
|
||||||
|
|
||||||
"page_projects_metadata_title": "پروژهها",
|
"page_projects_metadata_title": "پروژهها",
|
||||||
"page_projects_metadata_description": "نگاهی به پروژههای موفق ما که با استفاده از آجرهای با کیفیت اجرا شدهاند.",
|
"page_projects_metadata_description": "نگاهی به پروژههای موفق ما که با استفاده از آجرهای با کیفیت اجرا شدهاند.",
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import images from '@/assets/images/images';
|
import images from '@/assets/images/images';
|
||||||
import InnerPageBanner from '@/components/layout/innerPages/banner';
|
import InnerPageBanner from '@/components/layout/innerPages/banner';
|
||||||
|
import AboutDescription from '@/components/pages/about/AboutDescription';
|
||||||
import AboutTopInfo from '@/components/pages/about/TopInfo';
|
import AboutTopInfo from '@/components/pages/about/TopInfo';
|
||||||
|
import HomeStorySection from '@/components/pages/home/HomeStorySection';
|
||||||
import { IPageParams } from '@/models/layout';
|
import { IPageParams } from '@/models/layout';
|
||||||
import { setRequestLocale } from 'next-intl/server';
|
import { setRequestLocale } from 'next-intl/server';
|
||||||
import { getTranslations } from 'next-intl/server';
|
import { getTranslations } from 'next-intl/server';
|
||||||
@@ -26,12 +28,14 @@ export default async function AboutUs({
|
|||||||
const t = await getTranslations({ locale });
|
const t = await getTranslations({ locale });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="flex flex-col items-center justify-between">
|
<main className="mb-10 flex flex-col items-center justify-between">
|
||||||
<InnerPageBanner
|
<InnerPageBanner
|
||||||
title={t('pages_about_title')}
|
title={t('pages_about_title')}
|
||||||
imageSrc={images.aboutBanner.src}
|
imageSrc={images.aboutBanner.src}
|
||||||
/>
|
/>
|
||||||
<AboutTopInfo />
|
<AboutTopInfo />
|
||||||
|
<AboutDescription className="mb-8 md:mb-14" locale={locale} />
|
||||||
|
<HomeStorySection />
|
||||||
</main>
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
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 ContentGenerator from '@/components/layout/contentGenerator';
|
||||||
|
import InnerPageBanner from '@/components/layout/innerPages/banner';
|
||||||
|
import { ILayoutLocaleParams } from '@/models/layout';
|
||||||
|
import { getPost } from '@/services/blog';
|
||||||
|
import { getTranslations } from 'next-intl/server';
|
||||||
|
|
||||||
|
interface IPageParams extends ILayoutLocaleParams {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function PostPage({ params }: { params: IPageParams }) {
|
||||||
|
const paramsData = await params;
|
||||||
|
const t = await getTranslations({ locale: paramsData.locale });
|
||||||
|
|
||||||
|
const { id: postId, locale } = await params;
|
||||||
|
const routes = routeFactory(t, locale);
|
||||||
|
|
||||||
|
const postData = await getPost(postId);
|
||||||
|
|
||||||
|
const breadcrumbItems = [
|
||||||
|
{
|
||||||
|
...routes.blog,
|
||||||
|
href: routes.blog.route(),
|
||||||
|
},
|
||||||
|
{ title: postData?.title },
|
||||||
|
] as IBreadcrumbItem[];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main className="flex flex-col items-center justify-between">
|
||||||
|
<InnerPageBanner
|
||||||
|
title={t('pages_blog_title')}
|
||||||
|
imageSrc={images.blogBanner.src}
|
||||||
|
/>
|
||||||
|
<div className="container-fluid mx-auto my-12 md:my-24">
|
||||||
|
<Breadcrumb items={breadcrumbItems} />
|
||||||
|
{postData.content && postData.content.length ? (
|
||||||
|
<ContentGenerator data={postData.content} className="mt-10" />
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -31,7 +31,7 @@ export default async function Contact({ params }: { params: IPageParams }) {
|
|||||||
imageSrc={images.blogBanner.src}
|
imageSrc={images.blogBanner.src}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="relative flex w-full flex-col bg-gray-200 pt-4 md:pt-28">
|
<div className="relative flex w-full flex-col bg-gray-200 pt-12 lg:pt-28">
|
||||||
<div className="absolute inset-x-0 inset-y-64 z-2 flex items-end justify-start">
|
<div className="absolute inset-x-0 inset-y-64 z-2 flex items-end justify-start">
|
||||||
<div className="max-md:hidden md:w-1/2">
|
<div className="max-md:hidden md:w-1/2">
|
||||||
<Image
|
<Image
|
||||||
@@ -41,9 +41,9 @@ export default async function Contact({ params }: { params: IPageParams }) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="container mx-auto flex w-full flex-col items-stretch overflow-hidden rounded-lg md:flex-row">
|
<div className="container mx-auto flex w-full flex-col items-stretch overflow-hidden rounded-lg lg:flex-row">
|
||||||
<TouchUs />
|
<TouchUs className="p-8 lg:w-1/2" />
|
||||||
<ContactForm />
|
<ContactForm className="p-4 lg:w-1/2" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-4 w-full md:mt-28">
|
<div className="mt-4 w-full md:mt-28">
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
import { NextResponse } from 'next/server';
|
||||||
|
import { cookies } from 'next/headers';
|
||||||
|
import { posts } from '../data';
|
||||||
|
|
||||||
|
export async function GET(request: Request, context: any) {
|
||||||
|
const params = await context.params;
|
||||||
|
const postId = params.id;
|
||||||
|
|
||||||
|
const cookieStore = await cookies();
|
||||||
|
const lang = cookieStore.get('locale')?.value || 'en';
|
||||||
|
|
||||||
|
let post = posts.find((post) => post.id === postId);
|
||||||
|
if (!post) {
|
||||||
|
return NextResponse.json({ error: 'Not found' }, { status: 404 });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lang === 'fa') {
|
||||||
|
post = {
|
||||||
|
...post,
|
||||||
|
title: post.fa_title || post.title,
|
||||||
|
content: post.fa_content || post.content,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.json({
|
||||||
|
data: post,
|
||||||
|
});
|
||||||
|
}
|
||||||
+126
-16
@@ -1,59 +1,169 @@
|
|||||||
import images from '@/assets/images/images';
|
import images from '@/assets/images/images';
|
||||||
|
import { IDynamicContent } from '@/models/dynamicContent';
|
||||||
|
|
||||||
export interface IPostCardProps {
|
export interface IPostThumbResponseData {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
imageSrc: string;
|
||||||
|
}
|
||||||
|
export interface IPostResponseData extends IPostThumbResponseData {
|
||||||
|
content: IDynamicContent[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IPostData {
|
||||||
id: string;
|
id: string;
|
||||||
title: string;
|
title: string;
|
||||||
fa_title?: string;
|
fa_title?: string;
|
||||||
imageSrc: string;
|
imageSrc: string;
|
||||||
link: string;
|
content: IDynamicContent[];
|
||||||
|
fa_content: IDynamicContent[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const posts: IPostCardProps[] = [
|
export const posts: IPostData[] = [
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
title: 'Sustainable Practices Reducing Waste in Industrial Production',
|
title: 'Sustainable Practices Reducing Waste in Industrial Production',
|
||||||
fa_title: 'روشهای پایدار برای کاهش ضایعات در تولید صنعتی',
|
fa_title: 'روشهای پایدار برای کاهش ضایعات در تولید صنعتی',
|
||||||
imageSrc: images.blogImg_1.src,
|
imageSrc: '/images/project1.jpeg',
|
||||||
link: '#',
|
content: [
|
||||||
|
{
|
||||||
|
type: 'title',
|
||||||
|
data: 'روشهای پایدار برای کاهش ضایعات در تولید صنعتی',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'content',
|
||||||
|
data: 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'bullet',
|
||||||
|
data: [
|
||||||
|
'روشهای پایدار برای کاهش ضایعات در تولید صنعتی',
|
||||||
|
'رباتیک پیشرفته و تحول فرآیندهای صنعتی',
|
||||||
|
'مزایای اصلی استفاده از رباتیک در تولید',
|
||||||
|
'استفاده از تحلیل داده برای تولید هوشمندتر',
|
||||||
|
'کاهش هزینهها با اتوماسیون',
|
||||||
|
'مزایای راهحلهای سفارشیسازی در تولید',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'image',
|
||||||
|
data: {
|
||||||
|
src: '/images/project2.jpeg',
|
||||||
|
alt: '',
|
||||||
|
thumbTitle:
|
||||||
|
'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'numberList',
|
||||||
|
data: [
|
||||||
|
'روشهای پایدار برای کاهش ضایعات در تولید صنعتی',
|
||||||
|
'رباتیک پیشرفته و تحول فرآیندهای صنعتی',
|
||||||
|
'مزایای اصلی استفاده از رباتیک در تولید',
|
||||||
|
'استفاده از تحلیل داده برای تولید هوشمندتر',
|
||||||
|
'کاهش هزینهها با اتوماسیون',
|
||||||
|
'مزایای راهحلهای سفارشیسازی در تولید',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
fa_content: [
|
||||||
|
{
|
||||||
|
type: 'title',
|
||||||
|
data: 'روشهای پایدار برای کاهش ضایعات در تولید صنعتی',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'content',
|
||||||
|
data: {
|
||||||
|
links: [
|
||||||
|
{
|
||||||
|
link: '/',
|
||||||
|
key: '_link',
|
||||||
|
label: 'ایپسوم',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
data: 'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. لورم ایپسوم\n متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. لورم _link متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است. لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده\n از طراحان گرافیک است. لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'bullet',
|
||||||
|
data: [
|
||||||
|
'روشهای پایدار برای کاهش ضایعات در تولید صنعتی',
|
||||||
|
'رباتیک پیشرفته و تحول فرآیندهای صنعتی',
|
||||||
|
'مزایای اصلی استفاده از رباتیک در تولید',
|
||||||
|
'استفاده از تحلیل داده برای تولید هوشمندتر',
|
||||||
|
'کاهش هزینهها با اتوماسیون',
|
||||||
|
'مزایای راهحلهای سفارشیسازی در تولید',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'image',
|
||||||
|
data: {
|
||||||
|
src: '/images/project1.jpeg',
|
||||||
|
alt: '',
|
||||||
|
thumbTitle:
|
||||||
|
'لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'title',
|
||||||
|
data: 'روشهای پایدار برای کاهش ضایعات در تولید صنعتی',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'numberList',
|
||||||
|
data: [
|
||||||
|
'روشهای پایدار برای کاهش ضایعات در تولید صنعتی',
|
||||||
|
'رباتیک پیشرفته و تحول فرآیندهای صنعتی',
|
||||||
|
'مزایای اصلی استفاده از رباتیک در تولید',
|
||||||
|
'استفاده از تحلیل داده برای تولید هوشمندتر',
|
||||||
|
'کاهش هزینهها با اتوماسیون',
|
||||||
|
'مزایای راهحلهای سفارشیسازی در تولید',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
title: 'Advanced Robotics Revolutionizing Industrial Workflows',
|
title: 'Advanced Robotics Revolutionizing Industrial Workflows',
|
||||||
fa_title: 'رباتیک پیشرفته و تحول فرآیندهای صنعتی',
|
fa_title: 'رباتیک پیشرفته و تحول فرآیندهای صنعتی',
|
||||||
imageSrc: images.blogImg_1.src,
|
imageSrc: '/images/project2.jpeg',
|
||||||
link: '#',
|
content: [],
|
||||||
|
fa_content: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '3',
|
id: '3',
|
||||||
title: 'Top Benefits of the Robotics in Manufacturing',
|
title: 'Top Benefits of the Robotics in Manufacturing',
|
||||||
fa_title: 'مزایای اصلی استفاده از رباتیک در تولید',
|
fa_title: 'مزایای اصلی استفاده از رباتیک در تولید',
|
||||||
imageSrc: images.blogImg_1.src,
|
imageSrc: '/images/project4.jpeg',
|
||||||
link: '#',
|
content: [],
|
||||||
|
fa_content: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '4',
|
id: '4',
|
||||||
title: 'Leveraging Data Analytics for Smarter Production',
|
title: 'Leveraging Data Analytics for Smarter Production',
|
||||||
fa_title: 'استفاده از تحلیل داده برای تولید هوشمندتر',
|
fa_title: 'استفاده از تحلیل داده برای تولید هوشمندتر',
|
||||||
imageSrc: images.blogImg_1.src,
|
imageSrc: '/images/project3.jpeg',
|
||||||
link: '#',
|
content: [],
|
||||||
|
fa_content: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '5',
|
id: '5',
|
||||||
title: 'Reducing Operational Costs Through Automation',
|
title: 'Reducing Operational Costs Through Automation',
|
||||||
fa_title: 'کاهش هزینهها با اتوماسیون',
|
fa_title: 'کاهش هزینهها با اتوماسیون',
|
||||||
imageSrc: '/images/post5.jpg',
|
imageSrc: '/images/project2.jpeg',
|
||||||
link: '#',
|
content: [],
|
||||||
|
fa_content: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '6',
|
id: '6',
|
||||||
title: 'The Advantages of Customized Manufacturing Solutions',
|
title: 'The Advantages of Customized Manufacturing Solutions',
|
||||||
fa_title: 'مزایای راهحلهای سفارشیسازی در تولید',
|
fa_title: 'مزایای راهحلهای سفارشیسازی در تولید',
|
||||||
imageSrc: '/images/post6.jpg',
|
imageSrc: '/images/project4.jpeg',
|
||||||
link: '#',
|
content: [],
|
||||||
|
fa_content: [],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export function generatePosts(): IPostCardProps[] {
|
export function generatePosts(): IPostData[] {
|
||||||
return [...posts, ...posts, ...posts].map((e, i) => ({
|
return [...posts, ...posts, ...posts].map((e, i) => ({
|
||||||
...e,
|
...e,
|
||||||
id: String(i + 1),
|
id: String(i + 1),
|
||||||
|
|||||||
+19
-23
@@ -1,37 +1,33 @@
|
|||||||
import { NextResponse } from 'next/server';
|
import { NextResponse } from 'next/server';
|
||||||
import { cookies } from 'next/headers';
|
import { cookies } from 'next/headers';
|
||||||
import { generatePosts, IPostCardProps } from './data';
|
import { generatePosts, IPostData } from './data';
|
||||||
|
|
||||||
export async function GET(request: Request) {
|
export async function GET(request: Request) {
|
||||||
const { searchParams } = new URL(request.url);
|
const { searchParams } = new URL(request.url);
|
||||||
const page = parseInt(searchParams.get('page') || '1', 10);
|
|
||||||
const limit = parseInt(searchParams.get('limit') || '6', 10);
|
|
||||||
const lang = (await cookies()).get('lang')?.value || 'fa';
|
const lang = (await cookies()).get('lang')?.value || 'fa';
|
||||||
|
const page = parseInt(searchParams.get('page') || '1', 10);
|
||||||
|
const perPage = 10;
|
||||||
|
|
||||||
let posts = generatePosts();
|
const posts = generatePosts();
|
||||||
|
const start = (page - 1) * perPage;
|
||||||
|
const end = start + perPage;
|
||||||
|
const paginatedPosts = posts.slice(start, end);
|
||||||
|
|
||||||
let data: IPostCardProps[] = posts.map(
|
const data = paginatedPosts.map((post) => {
|
||||||
({ id, title, fa_title, imageSrc, link }) => ({
|
return {
|
||||||
id,
|
id: post.id,
|
||||||
title: lang === 'fa' && fa_title ? fa_title : title,
|
imageSrc: post.imageSrc,
|
||||||
imageSrc,
|
title: post.fa_title || post.title,
|
||||||
link,
|
};
|
||||||
}),
|
});
|
||||||
);
|
|
||||||
|
|
||||||
const totalItems = data.length;
|
|
||||||
const totalPages = Math.ceil(totalItems / limit);
|
|
||||||
const startIndex = (page - 1) * limit;
|
|
||||||
const endIndex = startIndex + limit;
|
|
||||||
const paginatedData = data.slice(startIndex, endIndex);
|
|
||||||
|
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
data: paginatedData,
|
data,
|
||||||
pagination: {
|
pagination: {
|
||||||
currentPage: page,
|
page,
|
||||||
totalPages,
|
perPage,
|
||||||
totalItems,
|
total: posts.length,
|
||||||
limit,
|
totalPages: Math.ceil(posts.length / perPage),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ const filePath = path.resolve(process.cwd(), 'contactMessages.json');
|
|||||||
export async function POST(request: NextRequest) {
|
export async function POST(request: NextRequest) {
|
||||||
try {
|
try {
|
||||||
const body = await request.json();
|
const body = await request.json();
|
||||||
console.log(body)
|
|
||||||
const { first_name, last_name, phone, email, message } = body;
|
const { first_name, last_name, phone, email, message } = body;
|
||||||
|
|
||||||
if (!first_name || !last_name || !phone || !email || !message) {
|
if (!first_name || !last_name || !phone || !email || !message) {
|
||||||
@@ -44,8 +43,6 @@ export async function POST(request: NextRequest) {
|
|||||||
message: 'Your message has been received.',
|
message: 'Your message has been received.',
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
|
||||||
console.log(error)
|
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ error, message: 'مشکلی پیش آمده.' },
|
{ error, message: 'مشکلی پیش آمده.' },
|
||||||
{ status: 400 },
|
{ status: 400 },
|
||||||
|
|||||||
@@ -1,25 +1,30 @@
|
|||||||
import type { IProductCardProps } from '@/components/pages/products/productCard/productCard.d';
|
import type { IProductCardProps } from '@/components/pages/products/productCard/productCard.d';
|
||||||
import { productCategoriesData } from '../../data';
|
import { productCategoriesData } from '../../data';
|
||||||
|
import {
|
||||||
|
ghazaghiCategoryProducts,
|
||||||
|
ghermezCategoryProducts,
|
||||||
|
semiromCategoryProducts,
|
||||||
|
zardCategoryProducts,
|
||||||
|
} from './productsSeparatedByCode';
|
||||||
|
|
||||||
|
const productsRelatedByCategory = {
|
||||||
|
'1': ghazaghiCategoryProducts,
|
||||||
|
'2': ghermezCategoryProducts,
|
||||||
|
'3': zardCategoryProducts,
|
||||||
|
'40': semiromCategoryProducts,
|
||||||
|
};
|
||||||
|
|
||||||
// Helper to generate 50 products for a category
|
// Helper to generate 50 products for a category
|
||||||
export function generateProductsForCategory(
|
export function generateProductsForCategory(
|
||||||
categoryId: string,
|
categoryId: string,
|
||||||
categoryTitle: string,
|
|
||||||
): IProductCardProps[] {
|
): IProductCardProps[] {
|
||||||
return Array.from({ length: 50 }, (_, i) => ({
|
const relatedProducts = (productsRelatedByCategory[categoryId] ||
|
||||||
|
[]) as IProductCardProps[];
|
||||||
|
|
||||||
|
return Array.from(relatedProducts, (related, i) => ({
|
||||||
|
...related,
|
||||||
id: `${categoryId}-${i + 1}`,
|
id: `${categoryId}-${i + 1}`,
|
||||||
title: `${categoryTitle} Product ${i + 1}`,
|
title: `${related.model}`,
|
||||||
size: `W ${10 + i} * L${20 + i}`,
|
imageSrc: `/images/product${(i % 4) + 1}.png`,
|
||||||
imageSrc: `/images/product${(i % 4) + 1}.png`, // Example image path
|
|
||||||
// Add other fields from IProductCardProps if needed
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map of categoryId to products
|
|
||||||
export const productsByCategory: Record<string, IProductCardProps[]> =
|
|
||||||
Object.fromEntries(
|
|
||||||
productCategoriesData.map((cat) => [
|
|
||||||
cat.id,
|
|
||||||
generateProductsForCategory(cat.id, cat.title),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
|
|||||||
@@ -0,0 +1,461 @@
|
|||||||
|
export const zardCategoryProducts = [
|
||||||
|
{
|
||||||
|
model: 'پلاک ۷ معمولی',
|
||||||
|
color_code: 'زرد',
|
||||||
|
size: '7 * 33 * 2.5',
|
||||||
|
categoryId: '3',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک ۵.۵ معمولی',
|
||||||
|
color_code: 'زرد',
|
||||||
|
size: '5.5 * 26 * 2.5',
|
||||||
|
categoryId: '3',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک ۴ معمولی',
|
||||||
|
color_code: 'زرد',
|
||||||
|
size: '4 * 20 * 2.5',
|
||||||
|
categoryId: '3',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک ۵.۵ معمولی',
|
||||||
|
color_code: 'زرد',
|
||||||
|
size: '5.5 * 20 * 2.5',
|
||||||
|
categoryId: '3',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک ۵.۵ معمولی',
|
||||||
|
color_code: 'زرد',
|
||||||
|
size: '5.5 * 22 * 2.5',
|
||||||
|
categoryId: '3',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'ال ۷',
|
||||||
|
color_code: 'زرد',
|
||||||
|
size: '7 * 20 * 10 * 2.7',
|
||||||
|
categoryId: '3',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'ال ۴',
|
||||||
|
color_code: 'زرد',
|
||||||
|
size: '4 * 20 * 10 * 2.7',
|
||||||
|
categoryId: '3',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'ال نیمه ۴',
|
||||||
|
color_code: 'زرد',
|
||||||
|
size: '4 * 10 * 10 * 2.7',
|
||||||
|
categoryId: '3',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'ال نیمه ۵',
|
||||||
|
color_code: 'زرد',
|
||||||
|
size: '5.5 * 10 * 10 * 2.7',
|
||||||
|
categoryId: '3',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'ال ۵.۵',
|
||||||
|
color_code: 'زرد',
|
||||||
|
size: '5.5 * 20 * 10 * 2.7',
|
||||||
|
categoryId: '3',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'حصیری ۱۲',
|
||||||
|
color_code: 'زرد',
|
||||||
|
size: '4 * 12 * 2.5',
|
||||||
|
categoryId: '3',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'دولبگرد',
|
||||||
|
color_code: 'زرد',
|
||||||
|
size: '4 * 10 * 2.5',
|
||||||
|
categoryId: '3',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک ۵.۵ معمولی',
|
||||||
|
color_code: 'زرد',
|
||||||
|
size: '5.5 * 10 * 2.2',
|
||||||
|
categoryId: '3',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک معمولی ۴',
|
||||||
|
color_code: 'زرد',
|
||||||
|
size: '4 * 10 * 2.2',
|
||||||
|
categoryId: '3',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'لفتون',
|
||||||
|
color_code: 'زرد',
|
||||||
|
size: '10 * 21.5 * 5.5',
|
||||||
|
categoryId: '3',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک ۲۰',
|
||||||
|
color_code: 'زرد',
|
||||||
|
size: '20 * 20 * 2.5',
|
||||||
|
categoryId: '3',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'گلبرگ',
|
||||||
|
color_code: 'زرد',
|
||||||
|
size: '4 * 20 * 2.1',
|
||||||
|
categoryId: '3',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'الماسی',
|
||||||
|
color_code: 'زرد',
|
||||||
|
size: '4 * 20 * 2.1',
|
||||||
|
categoryId: '3',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const ghermezCategoryProducts = [
|
||||||
|
{
|
||||||
|
model: 'لفتون',
|
||||||
|
color_code: 'قرمز',
|
||||||
|
size: '10 * 21.5 * 5.5',
|
||||||
|
categoryId: '2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک ۵.۵',
|
||||||
|
color_code: 'قرمز',
|
||||||
|
size: '5.5 * 20 * 2.5',
|
||||||
|
categoryId: '2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک ۵',
|
||||||
|
color_code: 'قرمز',
|
||||||
|
size: '5.5 * 26 * 2.5',
|
||||||
|
categoryId: '2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک ۷',
|
||||||
|
color_code: 'قرمز',
|
||||||
|
size: '7 * 33 * 2.5',
|
||||||
|
categoryId: '2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'ال ۷',
|
||||||
|
color_code: 'قرمز',
|
||||||
|
size: '7 * 20 * 10 * 2.7',
|
||||||
|
categoryId: '2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک',
|
||||||
|
color_code: 'قرمز',
|
||||||
|
size: '5.5 * 10 * 2.2',
|
||||||
|
categoryId: '2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک',
|
||||||
|
color_code: 'قرمز',
|
||||||
|
size: '20 * 20 * 2.5',
|
||||||
|
categoryId: '2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'ال ۴',
|
||||||
|
color_code: 'قرمز',
|
||||||
|
size: '4 * 10 * 10 * 2.7',
|
||||||
|
categoryId: '2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'ال نیمه ۵',
|
||||||
|
color_code: 'قرمز',
|
||||||
|
size: '5.5 * 10 * 10 * 2.7',
|
||||||
|
categoryId: '2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'ال ۵',
|
||||||
|
color_code: 'قرمز',
|
||||||
|
size: '5.5 * 20 * 10 * 2.7',
|
||||||
|
categoryId: '2',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const semiromCategoryProducts = [
|
||||||
|
{
|
||||||
|
model: 'پافیلی',
|
||||||
|
color_code: 'سمیرم',
|
||||||
|
size: '10 * 20 * 2.5',
|
||||||
|
categoryId: '40',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک تایل',
|
||||||
|
color_code: 'سمیرم در ۴ رنگ',
|
||||||
|
size: '10 * 50 * 3',
|
||||||
|
categoryId: '40',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک ۵.۵',
|
||||||
|
color_code: 'سمیرم',
|
||||||
|
size: '5.5 * 26 * 2.5',
|
||||||
|
categoryId: '40',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک ۷',
|
||||||
|
color_code: 'سمیرم',
|
||||||
|
size: '7 * 33 * 2.5',
|
||||||
|
categoryId: '40',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'لفتون',
|
||||||
|
color_code: 'سمیرم',
|
||||||
|
size: '10 * 21.5 * 5.5',
|
||||||
|
categoryId: '40',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک',
|
||||||
|
color_code: 'سمیرم',
|
||||||
|
size: '10 * 20 * 2.5',
|
||||||
|
categoryId: '40',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'ال ۵.۵ و ال۷',
|
||||||
|
color_code: 'سمیرم در ۳ رنگ',
|
||||||
|
size: '5.5 * 10 * 20 * 2.5',
|
||||||
|
categoryId: '40',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک ۱۰',
|
||||||
|
color_code: 'سمیرم',
|
||||||
|
size: '10 * 10 * 2.5',
|
||||||
|
categoryId: '40',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک ۲۰',
|
||||||
|
color_code: 'سمیرم',
|
||||||
|
size: '20 * 20 * 2.5',
|
||||||
|
categoryId: '40',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک',
|
||||||
|
color_code: 'سمیرم در ۳ رنگ',
|
||||||
|
size: '10 * 20 * 5.5',
|
||||||
|
categoryId: '40',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'حلالی',
|
||||||
|
color_code: 'سمیرم',
|
||||||
|
size: '5.5 * 20 * 2.5',
|
||||||
|
categoryId: '40',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'حصیری',
|
||||||
|
color_code: 'سمیرم',
|
||||||
|
size: '5.5 * 20 * 2.5',
|
||||||
|
categoryId: '40',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک ۳',
|
||||||
|
color_code: 'سمیرم',
|
||||||
|
size: '3.5 * 33 * 3',
|
||||||
|
categoryId: '40',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: '',
|
||||||
|
color_code: 'سمیرم',
|
||||||
|
size: '',
|
||||||
|
categoryId: '40',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'دور ستون ۵۰',
|
||||||
|
color_code: 'سمیرم',
|
||||||
|
size: '',
|
||||||
|
categoryId: '40',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'دور ستون ۴۰',
|
||||||
|
color_code: 'سمیرم',
|
||||||
|
size: '',
|
||||||
|
categoryId: '40',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک چهارخونه ۷',
|
||||||
|
color_code: 'سمیرم',
|
||||||
|
size: '7 * 33 * 2.5',
|
||||||
|
categoryId: '40',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک شبکهای ۷',
|
||||||
|
color_code: 'سمیرم',
|
||||||
|
size: '7 * 33 * 2.5',
|
||||||
|
categoryId: '40',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'سر ستون',
|
||||||
|
color_code: 'سمیرم',
|
||||||
|
size: '',
|
||||||
|
categoryId: '40',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک مختط ۷',
|
||||||
|
color_code: 'سمیرم',
|
||||||
|
size: '7 * 33 * 2.5',
|
||||||
|
categoryId: '40',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک شیاردار ۷',
|
||||||
|
color_code: 'سمیرم',
|
||||||
|
size: '7 * 33 * 2.5',
|
||||||
|
categoryId: '40',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک نیلوفر ۱۰',
|
||||||
|
color_code: 'سمیرم',
|
||||||
|
size: '10 * 10 * 2.5',
|
||||||
|
categoryId: '40',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک خطی ۱۰',
|
||||||
|
color_code: 'سمیرم',
|
||||||
|
size: '10 * 10 * 2.5',
|
||||||
|
categoryId: '40',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'دور قاب',
|
||||||
|
color_code: 'سمیرم',
|
||||||
|
size: '10 * 20 * 2.5',
|
||||||
|
categoryId: '40',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const shamootiCategoryProducts = [
|
||||||
|
{
|
||||||
|
model: 'دورستون ۵۰',
|
||||||
|
color_code: 'شاموتی',
|
||||||
|
size: '',
|
||||||
|
categoryId: '41',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک ۱۰',
|
||||||
|
color_code: 'شاموتی در ۲ رنگ',
|
||||||
|
size: '10 * 20 * 2.5',
|
||||||
|
categoryId: '41',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک ۲۶',
|
||||||
|
color_code: 'شاموتی در ۳ رنگ',
|
||||||
|
size: '5.5 * 26 * 2.5',
|
||||||
|
categoryId: '41',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک ۷',
|
||||||
|
color_code: 'شاموتی در ۳ رنگ',
|
||||||
|
size: '7 * 33 * 2.5',
|
||||||
|
categoryId: '41',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک دایره',
|
||||||
|
color_code: 'شاموتی',
|
||||||
|
size: '10 * 10 * 2.5',
|
||||||
|
categoryId: '41',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک لب قاشقی',
|
||||||
|
color_code: 'شاموتی',
|
||||||
|
size: '10 * 10 * 2.5',
|
||||||
|
categoryId: '41',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'دور قاب',
|
||||||
|
color_code: 'شاموتی',
|
||||||
|
size: '10 * 20 * 2.5',
|
||||||
|
categoryId: '41',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'حلالی',
|
||||||
|
color_code: 'شاموتی',
|
||||||
|
size: '5.5 * 20 * 2.5',
|
||||||
|
categoryId: '41',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک ۳',
|
||||||
|
color_code: 'شاموتی',
|
||||||
|
size: '3.5 * 33 * 3',
|
||||||
|
categoryId: '41',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک موج',
|
||||||
|
color_code: 'شاموتی',
|
||||||
|
size: '10 * 10 * 2.5',
|
||||||
|
categoryId: '41',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'ال ۷',
|
||||||
|
color_code: 'شاموتی',
|
||||||
|
size: '7 * 20 * 10 * 2.7',
|
||||||
|
categoryId: '41',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'ال ۵',
|
||||||
|
color_code: 'شاموتی',
|
||||||
|
size: '5.5 * 20 * 10 * 2.7',
|
||||||
|
categoryId: '41',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پاستونی و سرستونی',
|
||||||
|
color_code: 'شاموتی',
|
||||||
|
size: '',
|
||||||
|
categoryId: '41',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک ۲۰',
|
||||||
|
color_code: 'شاموتی',
|
||||||
|
size: '10 * 20 * 2.5',
|
||||||
|
categoryId: '41',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک ۱۰',
|
||||||
|
color_code: 'شاموتی',
|
||||||
|
size: '10 * 10 * 2.5',
|
||||||
|
categoryId: '41',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک معمولی ۲۰',
|
||||||
|
color_code: 'شاموتی',
|
||||||
|
size: '20 * 20 * 2.5',
|
||||||
|
categoryId: '41',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'دور ستون ۴۰',
|
||||||
|
color_code: 'شاموتی',
|
||||||
|
size: '',
|
||||||
|
categoryId: '41',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'پلاک ۱۰',
|
||||||
|
color_code: 'شاموتی در ۲ رنگ',
|
||||||
|
size: '10 * 20 * 5.5',
|
||||||
|
categoryId: '41',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'حصیری',
|
||||||
|
color_code: 'شاموتی',
|
||||||
|
size: '5.5 * 20 * 2.5',
|
||||||
|
categoryId: '41',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const sefidCategoryProducts = [
|
||||||
|
{
|
||||||
|
model: 'سرستون و پا ستون',
|
||||||
|
color_code: 'سفید',
|
||||||
|
size: '',
|
||||||
|
categoryId: '50',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: 'سرستون و پا ستون',
|
||||||
|
color_code: 'سفید',
|
||||||
|
size: '',
|
||||||
|
categoryId: '50',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const ghazaghiCategoryProducts = [
|
||||||
|
{
|
||||||
|
model: 'پافیلی',
|
||||||
|
color_code: 'سمیرم',
|
||||||
|
size: '10 * 20 * 2.5',
|
||||||
|
categoryId: '40',
|
||||||
|
},
|
||||||
|
];
|
||||||
@@ -14,12 +14,11 @@ export async function GET(request: NextRequest, context: any) {
|
|||||||
return NextResponse.json({ error: 'Not found' }, { status: 404 });
|
return NextResponse.json({ error: 'Not found' }, { status: 404 });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pagination logic
|
|
||||||
const { searchParams } = new URL(request.url);
|
const { searchParams } = new URL(request.url);
|
||||||
const page = parseInt(searchParams.get('page') || '1', 10);
|
const page = parseInt(searchParams.get('page') || '1', 10);
|
||||||
const perPage = 20;
|
const perPage = 5;
|
||||||
|
|
||||||
const allProducts = generateProductsForCategory(category.id, category.title);
|
const allProducts = generateProductsForCategory(category.id);
|
||||||
const start = (page - 1) * perPage;
|
const start = (page - 1) * perPage;
|
||||||
const end = start + perPage;
|
const end = start + perPage;
|
||||||
const paginatedProducts = allProducts.slice(start, end);
|
const paginatedProducts = allProducts.slice(start, end);
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ Facing Bricks: Characteristics and Features`,
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '3',
|
id: '3',
|
||||||
title: 'oryZard',
|
title: 'Zard',
|
||||||
fa_title: 'زرد',
|
fa_title: 'زرد',
|
||||||
icon: 'categoryZard',
|
icon: 'categoryZard',
|
||||||
typesCount: 11,
|
typesCount: 11,
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ export async function GET(request: Request) {
|
|||||||
|
|
||||||
const cookieStore = await cookies();
|
const cookieStore = await cookies();
|
||||||
const lang = (await cookieStore).get('locale')?.value || 'en';
|
const lang = (await cookieStore).get('locale')?.value || 'en';
|
||||||
console.log('!!!!!!!!!!!!!!!!!!!!');
|
|
||||||
console.log(lang);
|
|
||||||
|
|
||||||
if (lang === 'fa') {
|
if (lang === 'fa') {
|
||||||
data = productCategoriesData.map((productCategory) => ({
|
data = productCategoriesData.map((productCategory) => ({
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ export async function GET(request: Request) {
|
|||||||
|
|
||||||
// Pagination logic
|
// Pagination logic
|
||||||
const { searchParams } = new URL(request.url);
|
const { searchParams } = new URL(request.url);
|
||||||
|
|
||||||
const page = parseInt(searchParams.get('page') || '1', 10);
|
const page = parseInt(searchParams.get('page') || '1', 10);
|
||||||
const perPage = 20;
|
const perPage = 6;
|
||||||
|
|
||||||
const start = (page - 1) * perPage;
|
const start = (page - 1) * perPage;
|
||||||
const end = start + perPage;
|
const end = start + perPage;
|
||||||
@@ -31,7 +32,7 @@ export async function GET(request: Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
data: projects,
|
data: data,
|
||||||
pagination: {
|
pagination: {
|
||||||
page,
|
page,
|
||||||
perPage,
|
perPage,
|
||||||
|
|||||||
+1
-1
@@ -98,7 +98,7 @@ body.font-fa *{
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (width >=96rem) {
|
@media (width >=96rem) {
|
||||||
max-width: 96rem;
|
max-width: 90vw;
|
||||||
padding-inline: 8rem;
|
padding-inline: 8rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ type RouteFactory = (
|
|||||||
projects: { title: string; route: () => string };
|
projects: { title: string; route: () => string };
|
||||||
about: { title: string; route: () => string };
|
about: { title: string; route: () => string };
|
||||||
blog: { title: string; route: () => string };
|
blog: { title: string; route: () => string };
|
||||||
|
singlePost: { title: string; route: (postId: string) => string };
|
||||||
contact: { title: string; route: () => string };
|
contact: { title: string; route: () => string };
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -36,6 +37,10 @@ const routeFactory: RouteFactory = (t, locale) => ({
|
|||||||
title: t('pages_blog_title'),
|
title: t('pages_blog_title'),
|
||||||
route: () => `/${locale}/blog`,
|
route: () => `/${locale}/blog`,
|
||||||
},
|
},
|
||||||
|
singlePost: {
|
||||||
|
title: '',
|
||||||
|
route: (id: string) => `/${locale}/blog/${id}`,
|
||||||
|
},
|
||||||
contact: {
|
contact: {
|
||||||
title: t('pages_contact_title'),
|
title: t('pages_contact_title'),
|
||||||
route: () => `/${locale}/contact-us`,
|
route: () => `/${locale}/contact-us`,
|
||||||
|
|||||||
@@ -0,0 +1,107 @@
|
|||||||
|
import {
|
||||||
|
IContentData,
|
||||||
|
IContentDataLink,
|
||||||
|
IContentImage,
|
||||||
|
IDynamicContent,
|
||||||
|
} from '@/models/dynamicContent';
|
||||||
|
|
||||||
|
function ImageContent(data: IContentImage) {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center gap-3">
|
||||||
|
<img
|
||||||
|
src={data.src}
|
||||||
|
alt={data.alt}
|
||||||
|
className="mx-auto max-h-[60vh] max-w-[90vw] overflow-hidden rounded-2xl object-cover"
|
||||||
|
/>
|
||||||
|
{data.thumbTitle ? (
|
||||||
|
<h5 className="text-center text-sm text-gray-400">{data.thumbTitle}</h5>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function titleContent(data: string) {
|
||||||
|
return <h2 className="text-2xl font-bold text-gray-500">{data}</h2>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fullContent(data: IContentData) {
|
||||||
|
const splittedData = data.data?.split('\n') || [];
|
||||||
|
return splittedData.map((e) => {
|
||||||
|
return (
|
||||||
|
<p className="indent-5 text-base text-gray-400">
|
||||||
|
{generateLinkedContent(e, data.links)}
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateLinkedContent(data: string, links?: IContentDataLink[]) {
|
||||||
|
const splitted = data.split(' ');
|
||||||
|
|
||||||
|
const mappedLinks = {} as Record<string, IContentDataLink>;
|
||||||
|
links?.forEach((link) => {
|
||||||
|
mappedLinks[link.key] = link;
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<p>
|
||||||
|
{splitted.map((data) => {
|
||||||
|
return mappedLinks[data] ? (
|
||||||
|
<strong>
|
||||||
|
<a
|
||||||
|
href={mappedLinks[data].link}
|
||||||
|
className="text-primary hover:underline"
|
||||||
|
>
|
||||||
|
{mappedLinks[data].label}
|
||||||
|
</a>{' '}
|
||||||
|
</strong>
|
||||||
|
) : (
|
||||||
|
<>{data} </>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function listContent(data: string[], type: 'bullet' | 'numberList') {
|
||||||
|
return (
|
||||||
|
<ul>
|
||||||
|
{data.map((item, index) => (
|
||||||
|
<li
|
||||||
|
key={index}
|
||||||
|
className={`list-inside ${type === 'bullet' ? 'list-disc' : 'list-decimal'} text-sm text-gray-400`}
|
||||||
|
>
|
||||||
|
<h4 className="inline text-sm">{item}</h4>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function ContentGenerator({
|
||||||
|
data,
|
||||||
|
className,
|
||||||
|
}: {
|
||||||
|
data: IDynamicContent[];
|
||||||
|
className?: string;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className={`flex flex-col gap-3 md:gap-6 ${className}`}>
|
||||||
|
{data.map((e) => {
|
||||||
|
switch (e.type) {
|
||||||
|
case 'title':
|
||||||
|
return titleContent(e.data as string);
|
||||||
|
case 'content':
|
||||||
|
return <div>{fullContent(e.data as IContentData)}</div>;
|
||||||
|
case 'image':
|
||||||
|
return ImageContent(e.data as IContentImage);
|
||||||
|
case 'bullet':
|
||||||
|
case 'numberList':
|
||||||
|
return listContent(e.data as string[], e.type);
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -50,7 +50,7 @@ export default function Navbar() {
|
|||||||
/>
|
/>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<ul className="hidden space-x-6 text-sm md:flex lg:text-base">
|
<ul className="hidden space-x-6 text-sm lg:flex lg:text-base">
|
||||||
{links.map(({ route, title }) => (
|
{links.map(({ route, title }) => (
|
||||||
<li key={route()}>
|
<li key={route()}>
|
||||||
<Link
|
<Link
|
||||||
@@ -71,7 +71,7 @@ export default function Navbar() {
|
|||||||
|
|
||||||
{!open && (
|
{!open && (
|
||||||
<button
|
<button
|
||||||
className="text-white md:hidden"
|
className="text-white lg:hidden"
|
||||||
onClick={() => setOpen(true)}
|
onClick={() => setOpen(true)}
|
||||||
>
|
>
|
||||||
<Icon name="menu" className="h-6 w-6" />
|
<Icon name="menu" className="h-6 w-6" />
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { TLocales } from '@/models/layout';
|
||||||
|
import { useTranslations } from 'next-intl';
|
||||||
|
import { getTranslations } from 'next-intl/server';
|
||||||
|
|
||||||
|
export default async function AboutDescription({
|
||||||
|
locale,
|
||||||
|
className = '',
|
||||||
|
}: {
|
||||||
|
locale: TLocales;
|
||||||
|
className?: string;
|
||||||
|
}) {
|
||||||
|
const t = await getTranslations({ locale });
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`bg-primary w-full ${className}`}>
|
||||||
|
<div className="container-fluid mx-auto py-20">
|
||||||
|
<p className="text-xl text-white">{t('page_about_description')}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -6,22 +6,28 @@ import Pagination from '@/components/layout/pagination';
|
|||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { getPosts } from '@/services/blog';
|
import { getPosts } from '@/services/blog';
|
||||||
import { IPostCardProps } from '@/app/api/blog/data';
|
import { IPostData } from '@/app/api/blog/data';
|
||||||
|
import { IPagination } from '@/models/response';
|
||||||
|
import routeFactory from '@/assets/constants/routeFactory';
|
||||||
|
import { useParams } from 'next/navigation';
|
||||||
|
import { TLocales } from '@/models/layout';
|
||||||
|
import { useTranslations } from 'next-intl';
|
||||||
|
import Button from '@/components/uikit/button';
|
||||||
|
|
||||||
export interface IPostsGridProps {
|
export interface IPostsGridProps {
|
||||||
initialPosts: IPostCardProps[];
|
initialPosts: IPostData[];
|
||||||
initialPagination: {
|
initialPagination: IPagination;
|
||||||
currentPage: number;
|
|
||||||
totalPages: number;
|
|
||||||
totalItems: number;
|
|
||||||
limit: number;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function PostsGrid({
|
export default function PostsGrid({
|
||||||
initialPosts,
|
initialPosts,
|
||||||
initialPagination,
|
initialPagination,
|
||||||
}: IPostsGridProps) {
|
}: IPostsGridProps) {
|
||||||
|
const t = useTranslations();
|
||||||
|
const { locale } = useParams();
|
||||||
|
|
||||||
|
const routes = routeFactory(t, locale as TLocales);
|
||||||
|
|
||||||
const [posts, setPosts] = useState(initialPosts);
|
const [posts, setPosts] = useState(initialPosts);
|
||||||
const [pagination, setPagination] = useState(initialPagination);
|
const [pagination, setPagination] = useState(initialPagination);
|
||||||
|
|
||||||
@@ -40,7 +46,10 @@ export default function PostsGrid({
|
|||||||
<div className="container mx-auto px-4 py-14 md:py-28">
|
<div className="container mx-auto px-4 py-14 md:py-28">
|
||||||
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
<div className="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
||||||
{posts.map((post) => (
|
{posts.map((post) => (
|
||||||
<div key={post.id} className="overflow-hidden bg-white">
|
<div
|
||||||
|
key={post.id}
|
||||||
|
className="group flex flex-col gap-4 overflow-hidden bg-white md:gap-6"
|
||||||
|
>
|
||||||
<div className="relative aspect-[1.09] w-full overflow-hidden rounded-4xl">
|
<div className="relative aspect-[1.09] w-full overflow-hidden rounded-4xl">
|
||||||
<Image
|
<Image
|
||||||
src={post.imageSrc}
|
src={post.imageSrc}
|
||||||
@@ -48,16 +57,22 @@ export default function PostsGrid({
|
|||||||
fill
|
fill
|
||||||
className="h-auto w-full object-cover"
|
className="h-auto w-full object-cover"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div className="invisible absolute inset-0 flex items-center justify-center bg-black/20 opacity-0 backdrop-blur-xs transition-all group-hover:visible group-hover:opacity-100">
|
||||||
|
<Button
|
||||||
|
link={routes.singlePost.route(post.id)}
|
||||||
|
endIcon="showMore"
|
||||||
|
>
|
||||||
|
{t('page_blog_details_CTA')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="py-7">
|
<Link
|
||||||
<h2 className="mb-5 text-xl font-semibold">{post.title}</h2>
|
href={routes.singlePost.route(post.id)}
|
||||||
<a
|
className="group-hover:text-primary text-center text-gray-500"
|
||||||
href={post.link}
|
>
|
||||||
className="text-primary text-base hover:underline"
|
<h2 className="text-xl font-semibold">{post.title}</h2>
|
||||||
>
|
</Link>
|
||||||
Read More →
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,10 +4,9 @@ import { useState } from 'react';
|
|||||||
import Button from '@/components/uikit/button';
|
import Button from '@/components/uikit/button';
|
||||||
import FormField from '@/components/uikit/inputs';
|
import FormField from '@/components/uikit/inputs';
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
import api from '@/lib/axios';
|
|
||||||
import { setContact } from '@/services/contact';
|
import { setContact } from '@/services/contact';
|
||||||
|
|
||||||
const ContactForm = () => {
|
const ContactForm = ({ className = '' }) => {
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
|
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
@@ -57,7 +56,6 @@ const ContactForm = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSubmit = (e: React.FormEvent) => {
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
console.log(e);
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
setContact(formData).finally(() => {
|
setContact(formData).finally(() => {
|
||||||
@@ -68,7 +66,7 @@ const ContactForm = () => {
|
|||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
className="z-10 w-full rounded-4xl bg-white p-4 md:w-1/2"
|
className={`z-10 w-full rounded-4xl bg-white ${className}`}
|
||||||
>
|
>
|
||||||
<h2 className="mt-8 mb-10 text-5xl text-gray-800">
|
<h2 className="mt-8 mb-10 text-5xl text-gray-800">
|
||||||
{t('com_contact_title')}{' '}
|
{t('com_contact_title')}{' '}
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ import { TLocales } from '@/models/layout';
|
|||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
import { useParams } from 'next/navigation';
|
import { useParams } from 'next/navigation';
|
||||||
|
|
||||||
const TouchUs = () => {
|
const TouchUs = ({ className = '' }) => {
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const locale = params.locale as TLocales;
|
const locale = params.locale as TLocales;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full p-8 md:w-1/2">
|
<div className={`w-full ${className}`}>
|
||||||
<SectionTitle
|
<SectionTitle
|
||||||
title={t('pages_contact_title')}
|
title={t('pages_contact_title')}
|
||||||
description={t('com_contact_touch_title')}
|
description={t('com_contact_touch_title')}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import SectionTitle from '@/components/layout/sectionTitle';
|
|||||||
import { getTranslations } from 'next-intl/server';
|
import { getTranslations } from 'next-intl/server';
|
||||||
import { TLocales } from '@/models/layout';
|
import { TLocales } from '@/models/layout';
|
||||||
import { getProjects } from '@/services/projects';
|
import { getProjects } from '@/services/projects';
|
||||||
import { IProjectCardProps } from '../project/project';
|
|
||||||
import ProjectsGrid from '../project/ProjectGrid';
|
import ProjectsGrid from '../project/ProjectGrid';
|
||||||
import Button from '@/components/uikit/button';
|
import Button from '@/components/uikit/button';
|
||||||
import routeFactory from '@/assets/constants/routeFactory';
|
import routeFactory from '@/assets/constants/routeFactory';
|
||||||
@@ -12,14 +11,11 @@ interface Props {
|
|||||||
export default async function HomePageProjects({ locale }: Props) {
|
export default async function HomePageProjects({ locale }: Props) {
|
||||||
const t = await getTranslations({ locale });
|
const t = await getTranslations({ locale });
|
||||||
const projects = await getProjects();
|
const projects = await getProjects();
|
||||||
console.log(projects);
|
|
||||||
|
|
||||||
const routes = routeFactory(t, locale);
|
const routes = routeFactory(t, locale);
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<div
|
<div className="relative container mx-auto w-full bg-white">
|
||||||
className={`relative container mx-auto w-full bg-white bg-cover bg-fixed bg-center bg-no-repeat pt-28 max-lg:pt-6`}
|
|
||||||
>
|
|
||||||
<div className="relative z-20 grid grid-cols-1 lg:grid-cols-2 lg:gap-28">
|
<div className="relative z-20 grid grid-cols-1 lg:grid-cols-2 lg:gap-28">
|
||||||
<SectionTitle
|
<SectionTitle
|
||||||
title={t('pages_projects_title')}
|
title={t('pages_projects_title')}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
export interface IProductCardProps {
|
export interface IProductCardProps {
|
||||||
imageSrc: string;
|
id: string;
|
||||||
title: string;
|
model: string;
|
||||||
|
color_code: string;
|
||||||
size: string;
|
size: string;
|
||||||
|
categoryId: string;
|
||||||
|
imageSrc: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,11 +17,11 @@ export default function ProjectsGrid({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`grid grid-cols-1 gap-8 md:grid-cols-2 md:gap-12 xl:grid-cols-3 xl:gap-18 ${className}`}
|
className={`grid grid-cols-1 gap-6 md:grid-cols-2 md:gap-8 xl:grid-cols-3 xl:gap-10 ${className}`}
|
||||||
>
|
>
|
||||||
{projects.map((project, index) => (
|
{projects.map((project, index) => (
|
||||||
<div className="group flex flex-col gap-4 overflow-hidden bg-white md:gap-6">
|
<div className="group flex flex-col gap-3 overflow-hidden bg-white md:gap-5">
|
||||||
<div className="relative aspect-[1.4] w-full overflow-hidden rounded-4xl">
|
<div className="relative aspect-[1.4] w-full overflow-hidden rounded-2xl">
|
||||||
<Image
|
<Image
|
||||||
src={project.imageSrc}
|
src={project.imageSrc}
|
||||||
alt={project.title}
|
alt={project.title}
|
||||||
@@ -38,9 +38,9 @@ export default function ProjectsGrid({
|
|||||||
<Link
|
<Link
|
||||||
href={'/'}
|
href={'/'}
|
||||||
key={index}
|
key={index}
|
||||||
className="group-hover:text-primary text-center text-gray-500"
|
className="group-hover:text-primary mb-4 text-center text-gray-500"
|
||||||
>
|
>
|
||||||
<h2 className="text-xl font-semibold">{project.title}</h2>
|
<h2 className="text-base font-medium">{project.title}</h2>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ export default function ProjectsGridView({
|
|||||||
const [pagination, setPagination] = useState(initialPagination);
|
const [pagination, setPagination] = useState(initialPagination);
|
||||||
|
|
||||||
const fetchPage = (page: number) => {
|
const fetchPage = (page: number) => {
|
||||||
console.log(page);
|
|
||||||
|
|
||||||
getProjects(page)
|
getProjects(page)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
setProjects(res.data);
|
setProjects(res.data);
|
||||||
|
|||||||
+3
@@ -13,8 +13,11 @@ export interface IBaseButtonProps
|
|||||||
|
|
||||||
export interface ITextButtonProps extends IBaseButtonProps {
|
export interface ITextButtonProps extends IBaseButtonProps {
|
||||||
endIcon?: IconName;
|
endIcon?: IconName;
|
||||||
|
variant?: TButtonVariant;
|
||||||
}
|
}
|
||||||
export interface IIconButtonProps extends IBaseButtonProps {
|
export interface IIconButtonProps extends IBaseButtonProps {
|
||||||
color?: 'primary' | 'secondary';
|
color?: 'primary' | 'secondary';
|
||||||
size?: 'small' | 'medium' | 'large';
|
size?: 'small' | 'medium' | 'large';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type TButtonVariant = 'text' | 'solid';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { IIconButtonProps, ITextButtonProps } from './button';
|
import { IIconButtonProps, ITextButtonProps, TButtonVariant } from './button';
|
||||||
import { Icon } from '../icons';
|
import { Icon } from '../icons';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
@@ -11,14 +11,20 @@ const Button: React.FC<ITextButtonProps> = ({
|
|||||||
endIcon,
|
endIcon,
|
||||||
className,
|
className,
|
||||||
link,
|
link,
|
||||||
|
variant = 'solid',
|
||||||
onClick,
|
onClick,
|
||||||
...props
|
...props
|
||||||
}) => {
|
}) => {
|
||||||
const wrapperClass = `group bg-primary relative h-12 cursor-pointer rounded-[0.5625rem] p-[0.125rem] flex w-fit ${className || ''}`;
|
const wrapperClass = `group relative h-12 cursor-pointer rounded-[0.5625rem] p-[0.125rem] flex w-fit ${className || ''} ${variant === 'solid' ? 'bg-primary' : ''}`;
|
||||||
|
|
||||||
return link ? (
|
return link ? (
|
||||||
<Link href={link} className={wrapperClass} {...props}>
|
<Link href={link} className={wrapperClass} {...props}>
|
||||||
<ButtonBody {...props} loading={loading} endIcon={endIcon}>
|
<ButtonBody
|
||||||
|
{...props}
|
||||||
|
loading={loading}
|
||||||
|
endIcon={endIcon}
|
||||||
|
variant={variant}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</ButtonBody>
|
</ButtonBody>
|
||||||
</Link>
|
</Link>
|
||||||
@@ -29,17 +35,28 @@ const Button: React.FC<ITextButtonProps> = ({
|
|||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
className={wrapperClass}
|
className={wrapperClass}
|
||||||
>
|
>
|
||||||
<ButtonBody {...props} loading={loading} endIcon={endIcon}>
|
<ButtonBody
|
||||||
|
{...props}
|
||||||
|
loading={loading}
|
||||||
|
endIcon={endIcon}
|
||||||
|
variant={variant}
|
||||||
|
>
|
||||||
{children}
|
{children}
|
||||||
</ButtonBody>
|
</ButtonBody>
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const variantClassNames = (variant: TButtonVariant) =>
|
||||||
|
variant === 'solid'
|
||||||
|
? 'flex items-center justify-center rounded-lg bg-white text-gray-500'
|
||||||
|
: 'text-primary text-base hover:underline';
|
||||||
|
|
||||||
const ButtonBody: React.FC<ITextButtonProps> = ({
|
const ButtonBody: React.FC<ITextButtonProps> = ({
|
||||||
children,
|
children,
|
||||||
loading,
|
loading,
|
||||||
endIcon,
|
endIcon,
|
||||||
|
variant = 'solid',
|
||||||
}) => (
|
}) => (
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
{loading ? (
|
{loading ? (
|
||||||
@@ -65,11 +82,7 @@ const ButtonBody: React.FC<ITextButtonProps> = ({
|
|||||||
</svg>
|
</svg>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<div
|
<div className={`px-3 ${variantClassNames(variant)}`}>{children}</div>
|
||||||
className={`flex items-center justify-center rounded-lg bg-white px-3 text-gray-500`}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{endIcon && !loading ? (
|
{endIcon && !loading ? (
|
||||||
<span className="flex items-center justify-center p-3">
|
<span className="flex items-center justify-center p-3">
|
||||||
|
|||||||
Vendored
+30
@@ -0,0 +1,30 @@
|
|||||||
|
export interface IDynamicContent {
|
||||||
|
type: keyof IDynamicContentItem;
|
||||||
|
data: string[] | string | IContentImage | IContentData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IDynamicContentItem {
|
||||||
|
bullet?: string[];
|
||||||
|
numberList?: string[];
|
||||||
|
image?: IContentImage;
|
||||||
|
title?: string;
|
||||||
|
content?: IContentData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IContentImage {
|
||||||
|
src: string;
|
||||||
|
alt: string;
|
||||||
|
title?: string;
|
||||||
|
thumbTitle?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IContentData {
|
||||||
|
links?: IContentDataLink[];
|
||||||
|
data: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IContentDataLink {
|
||||||
|
key: string;
|
||||||
|
link: string;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
+18
-14
@@ -1,33 +1,37 @@
|
|||||||
'use server';
|
'use server';
|
||||||
|
|
||||||
import { IPostCardProps } from '@/app/api/blog/data';
|
import { IPostResponseData, IPostThumbResponseData } from '@/app/api/blog/data';
|
||||||
import api from '@/lib/axios';
|
import api from '@/lib/axios';
|
||||||
|
import { IResponse } from '@/models/response';
|
||||||
import { cookies } from 'next/headers';
|
import { cookies } from 'next/headers';
|
||||||
|
|
||||||
export interface IResponse<T> {
|
|
||||||
data: T;
|
|
||||||
pagination: {
|
|
||||||
currentPage: number;
|
|
||||||
totalPages: number;
|
|
||||||
totalItems: number;
|
|
||||||
limit: number;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getPosts(
|
export async function getPosts(
|
||||||
page = 1,
|
page = 1,
|
||||||
limit = 6,
|
): Promise<IResponse<IPostThumbResponseData[]>> {
|
||||||
): Promise<IResponse<IPostCardProps[]>> {
|
|
||||||
const cookiesStore = await cookies();
|
const cookiesStore = await cookies();
|
||||||
return api
|
return api
|
||||||
.get('/blog', {
|
.get('/blog', {
|
||||||
headers: {
|
headers: {
|
||||||
Cookie: cookiesStore.toString(),
|
Cookie: cookiesStore.toString(),
|
||||||
},
|
},
|
||||||
params: { page, limit },
|
params: { page },
|
||||||
})
|
})
|
||||||
.then(({ data }) => {
|
.then(({ data }) => {
|
||||||
return Promise.resolve(data);
|
return Promise.resolve(data);
|
||||||
})
|
})
|
||||||
.catch((error) => Promise.reject(error));
|
.catch((error) => Promise.reject(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getPost(postId: string): Promise<IPostResponseData> {
|
||||||
|
const cookiesStore = await cookies();
|
||||||
|
return api
|
||||||
|
.get(`/blog/${postId}`, {
|
||||||
|
headers: {
|
||||||
|
Cookie: cookiesStore.toString(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(({ data }) => {
|
||||||
|
return Promise.resolve(data.data);
|
||||||
|
})
|
||||||
|
.catch((error) => Promise.reject(error));
|
||||||
|
}
|
||||||
|
|||||||
@@ -41,6 +41,9 @@ export async function getProducts(
|
|||||||
page = 1,
|
page = 1,
|
||||||
): Promise<IResponse<IProductCardProps[]>> {
|
): Promise<IResponse<IProductCardProps[]>> {
|
||||||
const cookiesStore = await cookies();
|
const cookiesStore = await cookies();
|
||||||
|
console.log('request');
|
||||||
|
console.log(page);
|
||||||
|
|
||||||
return api
|
return api
|
||||||
.get(`/product_categories/${categoryId}/products`, {
|
.get(`/product_categories/${categoryId}/products`, {
|
||||||
headers: {
|
headers: {
|
||||||
|
|||||||
@@ -5,15 +5,13 @@ import { cookies } from 'next/headers';
|
|||||||
|
|
||||||
export async function getProjects(page = 1 as number) {
|
export async function getProjects(page = 1 as number) {
|
||||||
const cookiesStore = await cookies();
|
const cookiesStore = await cookies();
|
||||||
console.log('page');
|
|
||||||
console.log(page);
|
|
||||||
|
|
||||||
return api
|
return api
|
||||||
.get('/projects', {
|
.get('/projects', {
|
||||||
params: { page },
|
|
||||||
headers: {
|
headers: {
|
||||||
Cookie: cookiesStore.toString(),
|
Cookie: cookiesStore.toString(),
|
||||||
},
|
},
|
||||||
|
params: { page },
|
||||||
})
|
})
|
||||||
.then(({ data }) => Promise.resolve(data))
|
.then(({ data }) => Promise.resolve(data))
|
||||||
.catch((error) => Promise.reject(error));
|
.catch((error) => Promise.reject(error));
|
||||||
|
|||||||
Reference in New Issue
Block a user