create single project page and set blog section in home page

This commit is contained in:
2025-06-19 20:17:15 +03:30
parent e69e2bf7e1
commit d44e8037b1
25 changed files with 423 additions and 109 deletions
+12 -1
View File
@@ -12,6 +12,17 @@ interface IPageParams extends ILayoutLocaleParams {
id: string;
}
export async function generateMetadata({ params }: { params: IPageParams }) {
const { id: postId, locale } = await params;
const t = await getTranslations({ locale });
const postData = await getPost(postId);
return {
title: postData.title,
description: t('page_blog_metadata_description'),
};
}
export default async function PostPage({ params }: { params: IPageParams }) {
const paramsData = await params;
const t = await getTranslations({ locale: paramsData.locale });
@@ -32,7 +43,7 @@ export default async function PostPage({ params }: { params: IPageParams }) {
return (
<main className="flex flex-col items-center justify-between">
<InnerPageBanner
title={t('pages_blog_title')}
subtitle={postData.title}
imageSrc={images.blogBanner.src}
/>
<div className="container-fluid mx-auto my-12 md:my-24">
+3 -1
View File
@@ -1,5 +1,6 @@
import Header from '@/components/layout/header';
import AboutTopInfo from '@/components/pages/about/TopInfo';
import HomePageBlog from '@/components/pages/home/HomePageBlog';
import HomeStorySection from '@/components/pages/home/HomeStorySection';
import HomePageProducts from '@/components/pages/home/Products';
import HomePageProjects from '@/components/pages/home/Projects';
@@ -27,7 +28,8 @@ export default async function Home({ params }: { params: IPageParams }) {
<AboutTopInfo />
<HomePageProducts locale={locale} />
<HomePageProjects locale={locale} />
<HomeStorySection className="my-24 max-lg:my-12" />
<HomeStorySection className="my-12 lg:my-24" />
<HomePageBlog locale={locale} className="my-12 lg:my-24" />
</main>
);
}
+59
View File
@@ -0,0 +1,59 @@
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 { getProject } from '@/services/projects';
import { getTranslations } from 'next-intl/server';
interface IPageParams extends ILayoutLocaleParams {
id: string;
}
export async function generateMetadata({ params }: { params: IPageParams }) {
const { id: projectId, locale } = await params;
const t = await getTranslations({ locale });
const project = await getProject(projectId);
return {
title: project.title,
description: t('page_blog_metadata_description'),
};
}
export default async function ProjectPage({ params }: { params: IPageParams }) {
const paramsData = await params;
const t = await getTranslations({ locale: paramsData.locale });
const { id: projectId, locale } = await params;
const routes = routeFactory(t, locale);
const project = await getProject(projectId);
const breadcrumbItems = [
{
...routes.projects,
href: routes.projects.route(),
},
{ title: project?.title },
] as IBreadcrumbItem[];
return (
<main className="flex flex-col items-center justify-between">
<InnerPageBanner
subtitle={project.title}
imageSrc={images.projectImg_1.src}
/>
<div className="container-fluid mx-auto my-12 md:my-24">
<Breadcrumb items={breadcrumbItems} />
{project.content && project.content.length ? (
<ContentGenerator data={project.content} className="mt-10" />
) : (
<></>
)}
</div>
</main>
);
}
+1 -1
View File
@@ -1,4 +1,4 @@
import { IProjectCardProps } from '@/app/api/projects/data';
import { IProjectData } from '@/app/api/projects/data';
import routeFactory from '@/assets/constants/routeFactory';
import images from '@/assets/images/images';
import InnerPageBanner from '@/components/layout/innerPages/banner';
-1
View File
@@ -1,4 +1,3 @@
import images from '@/assets/images/images';
import { IDynamicContent } from '@/models/dynamicContent';
export interface IPostThumbResponseData {
@@ -7,7 +7,7 @@ import {
zardCategoryProducts,
} from './productsSeparatedByCode';
const productsRelatedByCategory = {
export const productsRelatedByCategory = {
'1': ghazaghiCategoryProducts,
'2': ghermezCategoryProducts,
'3': zardCategoryProducts,
@@ -4,14 +4,6 @@ import { NextResponse, NextRequest } from 'next/server';
import { cookies } from 'next/headers';
import { productCategoriesData } from '../data';
interface IContext {
params: {
id: string;
};
}
interface IParams {}
export async function GET(request: NextRequest, context: any) {
const cookieStore = await cookies();
const lang = cookieStore.get('locale')?.value || 'en';
+2 -12
View File
@@ -127,16 +127,6 @@ interface APIProductCategoryEntity {
fa_priceTypeTitle?: string;
bestForTitle: string;
fa_bestForTitle?: string;
fa_description?: `Facing Bricks: Characteristics and Features
Facing bricks, also known as face bricks or veneer bricks, are specially designed for visible areas of a building, primarily the exterior facades, but also for interior walls where aesthetics are important. Unlike common bricks which are often covered by plaster or other materials, facing bricks are chosen for their visual appeal and ability to withstand environmental conditions.
Facing Bricks: Characteristics and Features
Facing bricks, also known as face bricks or veneer bricks, are specially designed for visible areas of a building, primarily the exterior facades, but also for interior walls where aesthetics are important. Unlike common bricks which are often covered by plaster or other materials, facing bricks are chosen for their visual appeal and ability to withstand environmental conditions.
Facing Bricks: Characteristics and Features`;
description?: `Facing Bricks: Characteristics and Features
Facing bricks, also known as face bricks or veneer bricks, are specially designed for visible areas of a building, primarily the exterior facades, but also for interior walls where aesthetics are important. Unlike common bricks which are often covered by plaster or other materials, facing bricks are chosen for their visual appeal and ability to withstand environmental conditions.
Facing Bricks: Characteristics and Features
Facing bricks, also known as face bricks or veneer bricks, are specially designed for visible areas of a building, primarily the exterior facades, but also for interior walls where aesthetics are important. Unlike common bricks which are often covered by plaster or other materials, facing bricks are chosen for their visual appeal and ability to withstand environmental conditions.
Facing Bricks: Characteristics and Features`;
fa_description?: string;
description?: string;
}
+17 -12
View File
@@ -2,6 +2,18 @@ import { NextResponse } from 'next/server';
import { cookies } from 'next/headers';
import { APIProductCategoryEntity } from '@/models/api';
import { productCategoriesData } from './data';
import { productsRelatedByCategory } from './[id]/products/data';
const getTypesCount = (categoryId: string) =>
productsRelatedByCategory[categoryId]?.length || 0;
const setRelatedLocaleData = (productCategory: APIProductCategoryEntity) => ({
title: productCategory.fa_title || productCategory.title,
priceTypeTitle:
productCategory.fa_priceTypeTitle || productCategory.priceTypeTitle,
bestForTitle: productCategory.fa_bestForTitle || productCategory.bestForTitle,
description: productCategory.fa_description || productCategory.description,
});
export async function GET(request: Request) {
try {
@@ -10,18 +22,11 @@ export async function GET(request: Request) {
const cookieStore = await cookies();
const lang = (await cookieStore).get('locale')?.value || 'en';
if (lang === 'fa') {
data = productCategoriesData.map((productCategory) => ({
...productCategory,
title: (lang && productCategory.fa_title) || productCategory.title,
priceTypeTitle:
productCategory.fa_priceTypeTitle || productCategory.priceTypeTitle,
bestForTitle:
productCategory.fa_bestForTitle || productCategory.bestForTitle,
description:
productCategory.fa_description || productCategory.description,
}));
}
data = productCategoriesData.map((productCategory) => ({
...productCategory,
...(lang === 'fa' ? setRelatedLocaleData(productCategory) : {}),
typesCount: getTypesCount(productCategory.id),
}));
return NextResponse.json({ data });
} catch (error) {
+28
View File
@@ -0,0 +1,28 @@
import { NextResponse } from 'next/server';
import { cookies } from 'next/headers';
import { projects } from '../data';
export async function GET(request: Request, context: any) {
const params = await context.params;
const projectId = params.id;
const cookieStore = await cookies();
const lang = cookieStore.get('locale')?.value || 'en';
let project = projects.find((project) => project.id === projectId);
if (!project) {
return NextResponse.json({ error: 'Not found' }, { status: 404 });
}
if (lang === 'fa') {
project = {
...project,
title: project.fa_title || project.title,
content: project.fa_content || project.content,
};
}
return NextResponse.json({
data: project,
});
}
+22 -3
View File
@@ -1,10 +1,23 @@
import images from '@/assets/images/images';
import { tempContent } from '@/assets/temp/tempContent';
import { IDynamicContent } from '@/models/dynamicContent';
export interface IProjectCardProps {
export interface IProjectData {
id: string;
title: string;
fa_title?: string;
imageSrc: string;
content: IDynamicContent[];
fa_content: IDynamicContent[];
}
export interface IProjectThumbResponseData {
id: string;
title: string;
imageSrc: string;
}
export interface IProjectResponseData extends IProjectThumbResponseData {
content: IDynamicContent[];
}
export const projects = [
@@ -13,40 +26,46 @@ export const projects = [
title: 'Sustainable Practices Reducing Waste in Industrial Production',
fa_title: 'روش‌های پایدار برای کاهش ضایعات در تولید صنعتی',
imageSrc: `/images/project1.jpeg`,
...tempContent,
},
{
id: '2',
title: 'Advanced Robotics Revolutionizing Industrial Workflows',
fa_title: 'رباتیک پیشرفته و تحول فرآیندهای صنعتی',
imageSrc: `/images/project2.jpeg`,
...tempContent,
},
{
id: '3',
title: 'Top Benefits of the Robotics in Manufacturing',
fa_title: 'مزایای اصلی استفاده از رباتیک در تولید',
imageSrc: `/images/project3.jpeg`,
...tempContent,
},
{
id: '4',
title: 'Leveraging Data Analytics for Smarter Production',
fa_title: 'استفاده از تحلیل داده برای تولید هوشمندتر',
imageSrc: `/images/project4.jpeg`,
...tempContent,
},
{
id: '5',
title: 'Reducing Operational Costs Through Automation',
fa_title: 'کاهش هزینه‌ها با اتوماسیون',
imageSrc: `/images/project1.jpeg`,
...tempContent,
},
{
id: '6',
title: 'The Advantages of Customized Manufacturing Solutions',
fa_title: 'مزایای راه‌حل‌های سفارشی‌سازی در تولید',
imageSrc: `/images/project1.jpeg`,
...tempContent,
},
] as IProjectCardProps[];
] as IProjectData[];
export function generateProjects(): IProjectCardProps[] {
export function generateProjects(): IProjectData[] {
return [...projects, ...projects, ...projects, ...projects, ...projects].map(
(e, i) => ({
...e,
+18 -14
View File
@@ -1,10 +1,14 @@
import { NextResponse } from 'next/server';
import { cookies } from 'next/headers';
import { generateProjects } from './data';
import {
generateProjects,
IProjectData,
IProjectThumbResponseData,
} from './data';
export async function GET(request: Request) {
let projects = generateProjects();
let data = projects;
let data = projects as IProjectThumbResponseData[];
const cookieStore = await cookies();
const lang = (await cookieStore).get('lang')?.value || 'fa';
@@ -19,20 +23,20 @@ export async function GET(request: Request) {
const end = start + perPage;
const paginatedProjects = projects.slice(start, end);
if (lang === 'fa') {
data = paginatedProjects.map((project) => {
if (lang === 'fa') {
return {
...project,
title: project.fa_title || project.title,
};
}
return project;
});
}
const setRelatedLocaleData = (project: IProjectData) => ({
title: project.fa_title || project.title,
});
data = paginatedProjects.map((project) => ({
id: project.id,
imageSrc: project.imageSrc,
...(lang === 'fa'
? setRelatedLocaleData(project)
: { title: project.title }),
}));
return NextResponse.json({
data: data,
data,
pagination: {
page,
perPage,
@@ -6,6 +6,7 @@ type RouteFactory = (
productCategories: { title: string; route: () => string };
products: { title: string; route: (productId: string) => string };
projects: { title: string; route: () => string };
project: { title: string; route: (projectId: string) => string };
about: { title: string; route: () => string };
blog: { title: string; route: () => string };
singlePost: { title: string; route: (postId: string) => string };
@@ -29,6 +30,10 @@ const routeFactory: RouteFactory = (t, locale) => ({
title: t('pages_projects_title'),
route: () => `/${locale}/projects`,
},
project: {
title: '',
route: (projectId: string) => `/${locale}/projects/${projectId}`,
},
about: {
title: t('pages_about_title'),
route: () => `/${locale}/about-us`,
+97
View File
@@ -0,0 +1,97 @@
export const tempContent = {
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: [
'روش‌های پایدار برای کاهش ضایعات در تولید صنعتی',
'رباتیک پیشرفته و تحول فرآیندهای صنعتی',
'مزایای اصلی استفاده از رباتیک در تولید',
'استفاده از تحلیل داده برای تولید هوشمندتر',
'کاهش هزینه‌ها با اتوماسیون',
'مزایای راه‌حل‌های سفارشی‌سازی در تولید',
],
},
],
};
+2 -1
View File
@@ -1,4 +1,5 @@
export interface IInnerPageBannerProps {
imageSrc: string;
title: string;
title?: string;
subtitle?: string;
}
@@ -4,6 +4,7 @@ import { IInnerPageBannerProps } from './banner';
export default function InnerPageBanner({
title,
imageSrc,
subtitle,
}: IInnerPageBannerProps) {
return (
<header
@@ -13,7 +14,8 @@ export default function InnerPageBanner({
<div className="absolute inset-0 z-0 bg-black opacity-50" />
<Navbar />
<div className="relative z-10 container mx-auto flex flex-col bg-cover bg-right-top py-20 text-white">
<h2 className="text-[4rem] text-white">{title}</h2>
{title ? <h2 className="text-[4rem] text-white">{title}</h2> : <></>}
{subtitle ? <h2 className="text-lg text-white">{subtitle}</h2> : <></>}
</div>
</header>
);
+4 -35
View File
@@ -3,19 +3,17 @@
import { useState } from 'react';
import Pagination from '@/components/layout/pagination';
import Image from 'next/image';
import Link from 'next/link';
import { getPosts } from '@/services/blog';
import { IPostData } from '@/app/api/blog/data';
import { IPostThumbResponseData } 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';
import PostsGridView from './PostsGridView';
export interface IPostsGridProps {
initialPosts: IPostData[];
initialPosts: IPostThumbResponseData[];
initialPagination: IPagination;
}
@@ -45,36 +43,7 @@ export default function PostsGrid({
return (
<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="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}
alt={post.title}
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>
<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>
))}
<PostsGridView posts={posts} locale={locale as TLocales} />
</div>
<Pagination {...pagination} onPageChange={fetchPage} />
</div>
@@ -0,0 +1,53 @@
import { IPostThumbResponseData } from '@/app/api/blog/data';
import routeFactory from '@/assets/constants/routeFactory';
import Button from '@/components/uikit/button';
import { TLocales } from '@/models/layout';
import { getTranslations } from 'next-intl/server';
import Image from 'next/image';
import Link from 'next/link';
export default async function PostsGridView({
posts,
locale,
}: {
posts: IPostThumbResponseData[];
locale: TLocales;
}) {
const t = await getTranslations({ locale });
const routes = routeFactory(t, locale);
return (
<>
{posts.map((post) => (
<div
key={post.id}
className="group flex flex-col gap-3 overflow-hidden bg-white md:gap-5"
>
<div className="relative aspect-[1.09] w-full overflow-hidden rounded-2xl">
<Image
src={post.imageSrc}
alt={post.title}
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>
<Link
href={routes.singlePost.route(post.id)}
className="group-hover:text-primary mb-4 text-center text-gray-500"
>
<h2 className="text-base font-medium">{post.title}</h2>
</Link>
</div>
))}
</>
);
}
@@ -0,0 +1,54 @@
import SectionTitle from '@/components/layout/sectionTitle';
import { getTranslations } from 'next-intl/server';
import { TLocales } from '@/models/layout';
import Button from '@/components/uikit/button';
import routeFactory from '@/assets/constants/routeFactory';
import { getPosts } from '@/services/blog';
import PostsGridView from '../blog/PostsGridView';
interface Props {
locale: TLocales;
className?: string;
}
export default async function HomePageBlog({ locale, className }: Props) {
const t = await getTranslations({ locale });
const posts = await getPosts();
const routes = routeFactory(t, locale);
return (
<div className={`w-full ${className}`}>
<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_blog_title')}
description={t('com_home_blog_title')}
description_bold={t('com_home_blog_title_bold')}
locale={locale}
/>
<div className="pt-10 max-lg:hidden">
<h4 className="text-base font-extralight tracking-normal max-lg:hidden">
{t('com_home_blog_description')}
</h4>
<Button
link={routes.blog.route()}
endIcon="showMore"
className="mt-5"
>
{t('com_home_blog_show_more')}
</Button>
</div>
</div>
<div className="mt-10 grid grid-cols-1 gap-4 md:grid-cols-2 md:gap-6 lg:grid-cols-3 lg:gap-8">
<PostsGridView posts={posts.data?.slice(0, 3)} locale={locale} />
</div>
<Button
link={routes.blog.route()}
endIcon="showMore"
className="mx-auto mt-10 lg:hidden"
>
{t('com_home_blog_show_more')}
</Button>
</div>
</div>
);
}
+11 -2
View File
@@ -5,6 +5,9 @@ import Link from 'next/link';
import { IProjectCardProps } from './project';
import Button from '@/components/uikit/button';
import { useTranslations } from 'next-intl';
import routeFactory from '@/assets/constants/routeFactory';
import { useParams } from 'next/navigation';
import { TLocales } from '@/models/layout';
export default function ProjectsGrid({
projects,
@@ -14,6 +17,9 @@ export default function ProjectsGrid({
className?: string;
}) {
const t = useTranslations();
const { locale } = useParams();
const routes = routeFactory(t, locale as TLocales);
return (
<div
@@ -29,14 +35,17 @@ export default function ProjectsGrid({
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 endIcon="showMore">
<Button
endIcon="showMore"
link={routes.project.route(project.id)}
>
{t('com_home_projects_details_CTA')}
</Button>
</div>
</div>
<Link
href={'/'}
href={routes.project.route(project.id)}
key={index}
className="group-hover:text-primary mb-4 text-center text-gray-500"
>
+1 -1
View File
@@ -1,7 +1,7 @@
export interface IProjectCardProps {
imageSrc: string;
title: string;
link: string;
id: string;
}
export interface IProjectGridViewProps {
+16 -13
View File
@@ -1,5 +1,6 @@
'use server';
import { IProjectResponseData } from '@/app/api/projects/data';
import api from '@/lib/axios';
import { cookies } from 'next/headers';
@@ -17,16 +18,18 @@ export async function getProjects(page = 1 as number) {
.catch((error) => Promise.reject(error));
}
// export async function getProjectById(
// projectId: string,
// ): Promise<IProjectCardProps> {
// const cookiesStore = await cookies();
// return api
// .get(`/projects/${projectId}`, {
// headers: {
// Cookie: cookiesStore.toString(),
// },
// })
// .then(({ data }) => Promise.resolve(data.data))
// .catch((error) => Promise.reject(error));
// }
export async function getProject(
projectId: string,
): Promise<IProjectResponseData> {
const cookiesStore = await cookies();
return api
.get(`/projects/${projectId}`, {
headers: {
Cookie: cookiesStore.toString(),
},
})
.then(({ data }) => {
return Promise.resolve(data.data);
})
.catch((error) => Promise.reject(error));
}