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:
@@ -1,6 +1,8 @@
|
||||
import images from '@/assets/images/images';
|
||||
import InnerPageBanner from '@/components/layout/innerPages/banner';
|
||||
import AboutDescription from '@/components/pages/about/AboutDescription';
|
||||
import AboutTopInfo from '@/components/pages/about/TopInfo';
|
||||
import HomeStorySection from '@/components/pages/home/HomeStorySection';
|
||||
import { IPageParams } from '@/models/layout';
|
||||
import { setRequestLocale } from 'next-intl/server';
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
@@ -26,12 +28,14 @@ export default async function AboutUs({
|
||||
const t = await getTranslations({ locale });
|
||||
|
||||
return (
|
||||
<main className="flex flex-col items-center justify-between">
|
||||
<main className="mb-10 flex flex-col items-center justify-between">
|
||||
<InnerPageBanner
|
||||
title={t('pages_about_title')}
|
||||
imageSrc={images.aboutBanner.src}
|
||||
/>
|
||||
<AboutTopInfo />
|
||||
<AboutDescription className="mb-8 md:mb-14" locale={locale} />
|
||||
<HomeStorySection />
|
||||
</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}
|
||||
/>
|
||||
|
||||
<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="max-md:hidden md:w-1/2">
|
||||
<Image
|
||||
@@ -41,9 +41,9 @@ export default async function Contact({ params }: { params: IPageParams }) {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="container mx-auto flex w-full flex-col items-stretch overflow-hidden rounded-lg md:flex-row">
|
||||
<TouchUs />
|
||||
<ContactForm />
|
||||
<div className="container mx-auto flex w-full flex-col items-stretch overflow-hidden rounded-lg lg:flex-row">
|
||||
<TouchUs className="p-8 lg:w-1/2" />
|
||||
<ContactForm className="p-4 lg:w-1/2" />
|
||||
</div>
|
||||
|
||||
<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 { 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;
|
||||
title: string;
|
||||
fa_title?: string;
|
||||
imageSrc: string;
|
||||
link: string;
|
||||
content: IDynamicContent[];
|
||||
fa_content: IDynamicContent[];
|
||||
}
|
||||
|
||||
export const posts: IPostCardProps[] = [
|
||||
export const posts: IPostData[] = [
|
||||
{
|
||||
id: '1',
|
||||
title: 'Sustainable Practices Reducing Waste in Industrial Production',
|
||||
fa_title: 'روشهای پایدار برای کاهش ضایعات در تولید صنعتی',
|
||||
imageSrc: images.blogImg_1.src,
|
||||
link: '#',
|
||||
imageSrc: '/images/project1.jpeg',
|
||||
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',
|
||||
title: 'Advanced Robotics Revolutionizing Industrial Workflows',
|
||||
fa_title: 'رباتیک پیشرفته و تحول فرآیندهای صنعتی',
|
||||
imageSrc: images.blogImg_1.src,
|
||||
link: '#',
|
||||
imageSrc: '/images/project2.jpeg',
|
||||
content: [],
|
||||
fa_content: [],
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
title: 'Top Benefits of the Robotics in Manufacturing',
|
||||
fa_title: 'مزایای اصلی استفاده از رباتیک در تولید',
|
||||
imageSrc: images.blogImg_1.src,
|
||||
link: '#',
|
||||
imageSrc: '/images/project4.jpeg',
|
||||
content: [],
|
||||
fa_content: [],
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
title: 'Leveraging Data Analytics for Smarter Production',
|
||||
fa_title: 'استفاده از تحلیل داده برای تولید هوشمندتر',
|
||||
imageSrc: images.blogImg_1.src,
|
||||
link: '#',
|
||||
imageSrc: '/images/project3.jpeg',
|
||||
content: [],
|
||||
fa_content: [],
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
title: 'Reducing Operational Costs Through Automation',
|
||||
fa_title: 'کاهش هزینهها با اتوماسیون',
|
||||
imageSrc: '/images/post5.jpg',
|
||||
link: '#',
|
||||
imageSrc: '/images/project2.jpeg',
|
||||
content: [],
|
||||
fa_content: [],
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
title: 'The Advantages of Customized Manufacturing Solutions',
|
||||
fa_title: 'مزایای راهحلهای سفارشیسازی در تولید',
|
||||
imageSrc: '/images/post6.jpg',
|
||||
link: '#',
|
||||
imageSrc: '/images/project4.jpeg',
|
||||
content: [],
|
||||
fa_content: [],
|
||||
},
|
||||
];
|
||||
|
||||
export function generatePosts(): IPostCardProps[] {
|
||||
export function generatePosts(): IPostData[] {
|
||||
return [...posts, ...posts, ...posts].map((e, i) => ({
|
||||
...e,
|
||||
id: String(i + 1),
|
||||
|
||||
+19
-23
@@ -1,37 +1,33 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { cookies } from 'next/headers';
|
||||
import { generatePosts, IPostCardProps } from './data';
|
||||
import { generatePosts, IPostData } from './data';
|
||||
|
||||
export async function GET(request: Request) {
|
||||
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 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(
|
||||
({ id, title, fa_title, imageSrc, link }) => ({
|
||||
id,
|
||||
title: lang === 'fa' && fa_title ? fa_title : title,
|
||||
imageSrc,
|
||||
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);
|
||||
const data = paginatedPosts.map((post) => {
|
||||
return {
|
||||
id: post.id,
|
||||
imageSrc: post.imageSrc,
|
||||
title: post.fa_title || post.title,
|
||||
};
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
data: paginatedData,
|
||||
data,
|
||||
pagination: {
|
||||
currentPage: page,
|
||||
totalPages,
|
||||
totalItems,
|
||||
limit,
|
||||
page,
|
||||
perPage,
|
||||
total: posts.length,
|
||||
totalPages: Math.ceil(posts.length / perPage),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ const filePath = path.resolve(process.cwd(), 'contactMessages.json');
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const body = await request.json();
|
||||
console.log(body)
|
||||
const { first_name, last_name, phone, email, message } = body;
|
||||
|
||||
if (!first_name || !last_name || !phone || !email || !message) {
|
||||
@@ -44,8 +43,6 @@ export async function POST(request: NextRequest) {
|
||||
message: 'Your message has been received.',
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
console.log(error)
|
||||
return NextResponse.json(
|
||||
{ error, message: 'مشکلی پیش آمده.' },
|
||||
{ status: 400 },
|
||||
|
||||
@@ -1,25 +1,30 @@
|
||||
import type { IProductCardProps } from '@/components/pages/products/productCard/productCard.d';
|
||||
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
|
||||
export function generateProductsForCategory(
|
||||
categoryId: string,
|
||||
categoryTitle: string,
|
||||
): IProductCardProps[] {
|
||||
return Array.from({ length: 50 }, (_, i) => ({
|
||||
const relatedProducts = (productsRelatedByCategory[categoryId] ||
|
||||
[]) as IProductCardProps[];
|
||||
|
||||
return Array.from(relatedProducts, (related, i) => ({
|
||||
...related,
|
||||
id: `${categoryId}-${i + 1}`,
|
||||
title: `${categoryTitle} Product ${i + 1}`,
|
||||
size: `W ${10 + i} * L${20 + i}`,
|
||||
imageSrc: `/images/product${(i % 4) + 1}.png`, // Example image path
|
||||
// Add other fields from IProductCardProps if needed
|
||||
title: `${related.model}`,
|
||||
imageSrc: `/images/product${(i % 4) + 1}.png`,
|
||||
}));
|
||||
}
|
||||
|
||||
// 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 });
|
||||
}
|
||||
|
||||
// Pagination logic
|
||||
const { searchParams } = new URL(request.url);
|
||||
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 end = start + perPage;
|
||||
const paginatedProducts = allProducts.slice(start, end);
|
||||
|
||||
@@ -57,7 +57,7 @@ Facing Bricks: Characteristics and Features`,
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
title: 'oryZard',
|
||||
title: 'Zard',
|
||||
fa_title: 'زرد',
|
||||
icon: 'categoryZard',
|
||||
typesCount: 11,
|
||||
|
||||
@@ -9,8 +9,6 @@ export async function GET(request: Request) {
|
||||
|
||||
const cookieStore = await cookies();
|
||||
const lang = (await cookieStore).get('locale')?.value || 'en';
|
||||
console.log('!!!!!!!!!!!!!!!!!!!!');
|
||||
console.log(lang);
|
||||
|
||||
if (lang === 'fa') {
|
||||
data = productCategoriesData.map((productCategory) => ({
|
||||
|
||||
@@ -11,8 +11,9 @@ export async function GET(request: Request) {
|
||||
|
||||
// Pagination logic
|
||||
const { searchParams } = new URL(request.url);
|
||||
|
||||
const page = parseInt(searchParams.get('page') || '1', 10);
|
||||
const perPage = 20;
|
||||
const perPage = 6;
|
||||
|
||||
const start = (page - 1) * perPage;
|
||||
const end = start + perPage;
|
||||
@@ -31,7 +32,7 @@ export async function GET(request: Request) {
|
||||
}
|
||||
|
||||
return NextResponse.json({
|
||||
data: projects,
|
||||
data: data,
|
||||
pagination: {
|
||||
page,
|
||||
perPage,
|
||||
|
||||
+1
-1
@@ -98,7 +98,7 @@ body.font-fa *{
|
||||
}
|
||||
|
||||
@media (width >=96rem) {
|
||||
max-width: 96rem;
|
||||
max-width: 90vw;
|
||||
padding-inline: 8rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ type RouteFactory = (
|
||||
projects: { title: string; route: () => string };
|
||||
about: { title: string; route: () => string };
|
||||
blog: { title: string; route: () => string };
|
||||
singlePost: { title: string; route: (postId: string) => string };
|
||||
contact: { title: string; route: () => string };
|
||||
};
|
||||
|
||||
@@ -36,6 +37,10 @@ const routeFactory: RouteFactory = (t, locale) => ({
|
||||
title: t('pages_blog_title'),
|
||||
route: () => `/${locale}/blog`,
|
||||
},
|
||||
singlePost: {
|
||||
title: '',
|
||||
route: (id: string) => `/${locale}/blog/${id}`,
|
||||
},
|
||||
contact: {
|
||||
title: t('pages_contact_title'),
|
||||
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>
|
||||
|
||||
<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 }) => (
|
||||
<li key={route()}>
|
||||
<Link
|
||||
@@ -71,7 +71,7 @@ export default function Navbar() {
|
||||
|
||||
{!open && (
|
||||
<button
|
||||
className="text-white md:hidden"
|
||||
className="text-white lg:hidden"
|
||||
onClick={() => setOpen(true)}
|
||||
>
|
||||
<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 Link from 'next/link';
|
||||
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 {
|
||||
initialPosts: IPostCardProps[];
|
||||
initialPagination: {
|
||||
currentPage: number;
|
||||
totalPages: number;
|
||||
totalItems: number;
|
||||
limit: number;
|
||||
};
|
||||
initialPosts: IPostData[];
|
||||
initialPagination: IPagination;
|
||||
}
|
||||
|
||||
export default function PostsGrid({
|
||||
initialPosts,
|
||||
initialPagination,
|
||||
}: IPostsGridProps) {
|
||||
const t = useTranslations();
|
||||
const { locale } = useParams();
|
||||
|
||||
const routes = routeFactory(t, locale as TLocales);
|
||||
|
||||
const [posts, setPosts] = useState(initialPosts);
|
||||
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="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{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">
|
||||
<Image
|
||||
src={post.imageSrc}
|
||||
@@ -48,16 +57,22 @@ export default function PostsGrid({
|
||||
fill
|
||||
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 className="py-7">
|
||||
<h2 className="mb-5 text-xl font-semibold">{post.title}</h2>
|
||||
<a
|
||||
href={post.link}
|
||||
className="text-primary text-base hover:underline"
|
||||
>
|
||||
Read More →
|
||||
</a>
|
||||
</div>
|
||||
<Link
|
||||
href={routes.singlePost.route(post.id)}
|
||||
className="group-hover:text-primary text-center text-gray-500"
|
||||
>
|
||||
<h2 className="text-xl font-semibold">{post.title}</h2>
|
||||
</Link>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -4,10 +4,9 @@ import { useState } from 'react';
|
||||
import Button from '@/components/uikit/button';
|
||||
import FormField from '@/components/uikit/inputs';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import api from '@/lib/axios';
|
||||
import { setContact } from '@/services/contact';
|
||||
|
||||
const ContactForm = () => {
|
||||
const ContactForm = ({ className = '' }) => {
|
||||
const t = useTranslations();
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
@@ -57,7 +56,6 @@ const ContactForm = () => {
|
||||
};
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
console.log(e);
|
||||
e.preventDefault();
|
||||
setLoading(true);
|
||||
setContact(formData).finally(() => {
|
||||
@@ -68,7 +66,7 @@ const ContactForm = () => {
|
||||
return (
|
||||
<form
|
||||
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">
|
||||
{t('com_contact_title')}{' '}
|
||||
|
||||
@@ -6,13 +6,13 @@ import { TLocales } from '@/models/layout';
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { useParams } from 'next/navigation';
|
||||
|
||||
const TouchUs = () => {
|
||||
const TouchUs = ({ className = '' }) => {
|
||||
const t = useTranslations();
|
||||
const params = useParams();
|
||||
const locale = params.locale as TLocales;
|
||||
|
||||
return (
|
||||
<div className="w-full p-8 md:w-1/2">
|
||||
<div className={`w-full ${className}`}>
|
||||
<SectionTitle
|
||||
title={t('pages_contact_title')}
|
||||
description={t('com_contact_touch_title')}
|
||||
|
||||
@@ -2,7 +2,6 @@ import SectionTitle from '@/components/layout/sectionTitle';
|
||||
import { getTranslations } from 'next-intl/server';
|
||||
import { TLocales } from '@/models/layout';
|
||||
import { getProjects } from '@/services/projects';
|
||||
import { IProjectCardProps } from '../project/project';
|
||||
import ProjectsGrid from '../project/ProjectGrid';
|
||||
import Button from '@/components/uikit/button';
|
||||
import routeFactory from '@/assets/constants/routeFactory';
|
||||
@@ -12,14 +11,11 @@ interface Props {
|
||||
export default async function HomePageProjects({ locale }: Props) {
|
||||
const t = await getTranslations({ locale });
|
||||
const projects = await getProjects();
|
||||
console.log(projects);
|
||||
|
||||
const routes = routeFactory(t, locale);
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div
|
||||
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 container mx-auto w-full bg-white">
|
||||
<div className="relative z-20 grid grid-cols-1 lg:grid-cols-2 lg:gap-28">
|
||||
<SectionTitle
|
||||
title={t('pages_projects_title')}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
export interface IProductCardProps {
|
||||
imageSrc: string;
|
||||
title: string;
|
||||
id: string;
|
||||
model: string;
|
||||
color_code: string;
|
||||
size: string;
|
||||
categoryId: string;
|
||||
imageSrc: string;
|
||||
}
|
||||
|
||||
@@ -17,11 +17,11 @@ export default function ProjectsGrid({
|
||||
|
||||
return (
|
||||
<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) => (
|
||||
<div className="group flex flex-col gap-4 overflow-hidden bg-white md:gap-6">
|
||||
<div className="relative aspect-[1.4] w-full overflow-hidden rounded-4xl">
|
||||
<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-2xl">
|
||||
<Image
|
||||
src={project.imageSrc}
|
||||
alt={project.title}
|
||||
@@ -38,9 +38,9 @@ export default function ProjectsGrid({
|
||||
<Link
|
||||
href={'/'}
|
||||
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>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@@ -14,8 +14,6 @@ export default function ProjectsGridView({
|
||||
const [pagination, setPagination] = useState(initialPagination);
|
||||
|
||||
const fetchPage = (page: number) => {
|
||||
console.log(page);
|
||||
|
||||
getProjects(page)
|
||||
.then((res) => {
|
||||
setProjects(res.data);
|
||||
|
||||
+3
@@ -13,8 +13,11 @@ export interface IBaseButtonProps
|
||||
|
||||
export interface ITextButtonProps extends IBaseButtonProps {
|
||||
endIcon?: IconName;
|
||||
variant?: TButtonVariant;
|
||||
}
|
||||
export interface IIconButtonProps extends IBaseButtonProps {
|
||||
color?: 'primary' | 'secondary';
|
||||
size?: 'small' | 'medium' | 'large';
|
||||
}
|
||||
|
||||
export type TButtonVariant = 'text' | 'solid';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
import React from 'react';
|
||||
import { IIconButtonProps, ITextButtonProps } from './button';
|
||||
import { IIconButtonProps, ITextButtonProps, TButtonVariant } from './button';
|
||||
import { Icon } from '../icons';
|
||||
import Link from 'next/link';
|
||||
|
||||
@@ -11,14 +11,20 @@ const Button: React.FC<ITextButtonProps> = ({
|
||||
endIcon,
|
||||
className,
|
||||
link,
|
||||
variant = 'solid',
|
||||
onClick,
|
||||
...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 ? (
|
||||
<Link href={link} className={wrapperClass} {...props}>
|
||||
<ButtonBody {...props} loading={loading} endIcon={endIcon}>
|
||||
<ButtonBody
|
||||
{...props}
|
||||
loading={loading}
|
||||
endIcon={endIcon}
|
||||
variant={variant}
|
||||
>
|
||||
{children}
|
||||
</ButtonBody>
|
||||
</Link>
|
||||
@@ -29,17 +35,28 @@ const Button: React.FC<ITextButtonProps> = ({
|
||||
onClick={onClick}
|
||||
className={wrapperClass}
|
||||
>
|
||||
<ButtonBody {...props} loading={loading} endIcon={endIcon}>
|
||||
<ButtonBody
|
||||
{...props}
|
||||
loading={loading}
|
||||
endIcon={endIcon}
|
||||
variant={variant}
|
||||
>
|
||||
{children}
|
||||
</ButtonBody>
|
||||
</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> = ({
|
||||
children,
|
||||
loading,
|
||||
endIcon,
|
||||
variant = 'solid',
|
||||
}) => (
|
||||
<div className="flex">
|
||||
{loading ? (
|
||||
@@ -65,11 +82,7 @@ const ButtonBody: React.FC<ITextButtonProps> = ({
|
||||
</svg>
|
||||
) : null}
|
||||
|
||||
<div
|
||||
className={`flex items-center justify-center rounded-lg bg-white px-3 text-gray-500`}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
<div className={`px-3 ${variantClassNames(variant)}`}>{children}</div>
|
||||
|
||||
{endIcon && !loading ? (
|
||||
<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';
|
||||
|
||||
import { IPostCardProps } from '@/app/api/blog/data';
|
||||
import { IPostResponseData, IPostThumbResponseData } from '@/app/api/blog/data';
|
||||
import api from '@/lib/axios';
|
||||
import { IResponse } from '@/models/response';
|
||||
import { cookies } from 'next/headers';
|
||||
|
||||
export interface IResponse<T> {
|
||||
data: T;
|
||||
pagination: {
|
||||
currentPage: number;
|
||||
totalPages: number;
|
||||
totalItems: number;
|
||||
limit: number;
|
||||
};
|
||||
}
|
||||
|
||||
export async function getPosts(
|
||||
page = 1,
|
||||
limit = 6,
|
||||
): Promise<IResponse<IPostCardProps[]>> {
|
||||
): Promise<IResponse<IPostThumbResponseData[]>> {
|
||||
const cookiesStore = await cookies();
|
||||
return api
|
||||
.get('/blog', {
|
||||
headers: {
|
||||
Cookie: cookiesStore.toString(),
|
||||
},
|
||||
params: { page, limit },
|
||||
params: { page },
|
||||
})
|
||||
.then(({ data }) => {
|
||||
return Promise.resolve(data);
|
||||
})
|
||||
.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,
|
||||
): Promise<IResponse<IProductCardProps[]>> {
|
||||
const cookiesStore = await cookies();
|
||||
console.log('request');
|
||||
console.log(page);
|
||||
|
||||
return api
|
||||
.get(`/product_categories/${categoryId}/products`, {
|
||||
headers: {
|
||||
|
||||
@@ -5,15 +5,13 @@ import { cookies } from 'next/headers';
|
||||
|
||||
export async function getProjects(page = 1 as number) {
|
||||
const cookiesStore = await cookies();
|
||||
console.log('page');
|
||||
console.log(page);
|
||||
|
||||
return api
|
||||
.get('/projects', {
|
||||
params: { page },
|
||||
headers: {
|
||||
Cookie: cookiesStore.toString(),
|
||||
},
|
||||
params: { page },
|
||||
})
|
||||
.then(({ data }) => Promise.resolve(data))
|
||||
.catch((error) => Promise.reject(error));
|
||||
|
||||
Reference in New Issue
Block a user