mirror of
https://git.meshkee.com/Meshkee-Websites/arad-arisman.git
synced 2026-08-11 20:29:33 +04:30
- Add src/lib/meshkee.ts API client for the live backend (https://api.meshkee.com, tenant aradarisman.com) - Products list/detail pages now fetch real catalog data, images, categories and pricing instead of static mock data - Blog list/detail pages fetch from /tenants/{domain}/blogs with a graceful empty state since no posts are published yet - Allow the Meshkee media CDN host in next.config.ts - Remove now-unused static src/data/products.ts and posts.ts
143 lines
4.4 KiB
TypeScript
143 lines
4.4 KiB
TypeScript
import type { Metadata } from "next";
|
|
import Image from "next/image";
|
|
import Link from "next/link";
|
|
import { notFound } from "next/navigation";
|
|
import Container from "@/components/Container";
|
|
import PageBanner from "@/components/PageBanner";
|
|
import PostCard from "@/components/PostCard";
|
|
import {
|
|
getPost,
|
|
getPosts,
|
|
postContent,
|
|
postDate,
|
|
postExcerpt,
|
|
postImage,
|
|
postReadingTime,
|
|
postTitle,
|
|
} from "@/lib/meshkee";
|
|
|
|
export const revalidate = 300;
|
|
|
|
export async function generateStaticParams() {
|
|
const { items } = await getPosts({ page: 1, pageSize: 100 });
|
|
return items.map((p) => ({ slug: p.slug }));
|
|
}
|
|
|
|
export async function generateMetadata({
|
|
params,
|
|
}: {
|
|
params: Promise<{ slug: string }>;
|
|
}): Promise<Metadata> {
|
|
const { slug } = await params;
|
|
const post = await getPost(slug);
|
|
if (!post) return {};
|
|
return {
|
|
title: `${postTitle(post)} | آراد آریسمان`,
|
|
description: postExcerpt(post, 160),
|
|
};
|
|
}
|
|
|
|
export default async function BlogDetailPage({
|
|
params,
|
|
}: {
|
|
params: Promise<{ slug: string }>;
|
|
}) {
|
|
const { slug } = await params;
|
|
const post = await getPost(slug);
|
|
if (!post) notFound();
|
|
|
|
const title = postTitle(post);
|
|
const image = postImage(post);
|
|
const content = postContent(post);
|
|
|
|
const related = post.categoryId
|
|
? (await getPosts({ categoryId: post.categoryId, pageSize: 4 })).items
|
|
.filter((p) => p.id !== post.id)
|
|
.slice(0, 3)
|
|
: [];
|
|
|
|
return (
|
|
<>
|
|
<PageBanner title={title} breadcrumb="مقالات" />
|
|
|
|
<section className="py-16 sm:py-20">
|
|
<Container className="max-w-3xl">
|
|
<Link
|
|
href="/blog"
|
|
className="inline-flex items-center gap-2 text-sm font-semibold text-primary hover:text-primary-dark transition-colors"
|
|
>
|
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none">
|
|
<path
|
|
d="M10.7 18.7a1 1 0 0 1-1.4 0l-6-6a1 1 0 0 1 0-1.4l6-6a1 1 0 1 1 1.4 1.4L6 11.3h14a1 1 0 1 1 0 2H6l4.7 4.3a1 1 0 0 1 0 1.4Z"
|
|
fill="currentColor"
|
|
/>
|
|
</svg>
|
|
بازگشت به مقالات
|
|
</Link>
|
|
|
|
<div className="mt-6 flex items-center gap-3 text-sm text-foreground/70">
|
|
{post.categoryNameFa && (
|
|
<span className="rounded-full bg-primary/5 px-3 py-1 text-xs font-semibold text-primary">
|
|
{post.categoryNameFa}
|
|
</span>
|
|
)}
|
|
<span>{postDate(post)}</span>
|
|
<span className="h-1 w-1 rounded-full bg-foreground/30" />
|
|
<span>{postReadingTime(post)}</span>
|
|
</div>
|
|
|
|
{image && (
|
|
<div className="relative mt-6 aspect-[16/9] rounded-3xl overflow-hidden shadow-xl bg-surface">
|
|
<Image
|
|
src={image}
|
|
alt={title}
|
|
fill
|
|
priority
|
|
className="object-cover"
|
|
sizes="(min-width: 1024px) 768px, 100vw"
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
{content && (
|
|
<div
|
|
className="rich-content mt-8"
|
|
dangerouslySetInnerHTML={{ __html: content }}
|
|
/>
|
|
)}
|
|
|
|
<div className="mt-10 rounded-2xl bg-surface p-6 flex flex-wrap items-center justify-between gap-4">
|
|
<div>
|
|
<p className="font-bold text-secondary">نویسنده: تیم آراد آریسمان</p>
|
|
<p className="mt-1 text-sm text-foreground">
|
|
برای مشاوره تخصصی تغذیه دام و طیور با ما در تماس باشید.
|
|
</p>
|
|
</div>
|
|
<Link
|
|
href="/contact"
|
|
className="rounded-full bg-primary px-6 py-3 text-white font-semibold hover:bg-primary-dark transition-colors shrink-0"
|
|
>
|
|
تماس با ما
|
|
</Link>
|
|
</div>
|
|
</Container>
|
|
</section>
|
|
|
|
{related.length > 0 && (
|
|
<section className="pb-16 sm:pb-20 bg-surface pt-16">
|
|
<Container>
|
|
<h2 className="text-2xl font-extrabold text-primary">
|
|
مقالات مرتبط
|
|
</h2>
|
|
<div className="mt-8 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
{related.map((p) => (
|
|
<PostCard key={p.id} post={p} />
|
|
))}
|
|
</div>
|
|
</Container>
|
|
</section>
|
|
)}
|
|
</>
|
|
);
|
|
}
|