Connect products and blog to Meshkee backend API

- 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
This commit is contained in:
2026-08-10 01:08:17 +03:30
parent e34fbe6e72
commit 3f479af8be
12 changed files with 500 additions and 709 deletions
+4
View File
@@ -7,6 +7,10 @@ const nextConfig: NextConfig = {
protocol: "https", protocol: "https",
hostname: "images.unsplash.com", hostname: "images.unsplash.com",
}, },
{
protocol: "https",
hostname: "c804387.parspack.net",
},
], ],
}, },
}; };
+54 -31
View File
@@ -5,10 +5,22 @@ import { notFound } from "next/navigation";
import Container from "@/components/Container"; import Container from "@/components/Container";
import PageBanner from "@/components/PageBanner"; import PageBanner from "@/components/PageBanner";
import PostCard from "@/components/PostCard"; import PostCard from "@/components/PostCard";
import { getPostBySlug, getRelatedPosts, posts } from "@/data/posts"; import {
getPost,
getPosts,
postContent,
postDate,
postExcerpt,
postImage,
postReadingTime,
postTitle,
} from "@/lib/meshkee";
export function generateStaticParams() { export const revalidate = 300;
return posts.map((p) => ({ slug: p.slug }));
export async function generateStaticParams() {
const { items } = await getPosts({ page: 1, pageSize: 100 });
return items.map((p) => ({ slug: p.slug }));
} }
export async function generateMetadata({ export async function generateMetadata({
@@ -17,11 +29,11 @@ export async function generateMetadata({
params: Promise<{ slug: string }>; params: Promise<{ slug: string }>;
}): Promise<Metadata> { }): Promise<Metadata> {
const { slug } = await params; const { slug } = await params;
const post = getPostBySlug(slug); const post = await getPost(slug);
if (!post) return {}; if (!post) return {};
return { return {
title: `${post.title} | آراد آریسمان`, title: `${postTitle(post)} | آراد آریسمان`,
description: post.excerpt, description: postExcerpt(post, 160),
}; };
} }
@@ -31,14 +43,22 @@ export default async function BlogDetailPage({
params: Promise<{ slug: string }>; params: Promise<{ slug: string }>;
}) { }) {
const { slug } = await params; const { slug } = await params;
const post = getPostBySlug(slug); const post = await getPost(slug);
if (!post) notFound(); if (!post) notFound();
const related = getRelatedPosts(post); 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 ( return (
<> <>
<PageBanner title={post.title} breadcrumb="مقالات" /> <PageBanner title={title} breadcrumb="مقالات" />
<section className="py-16 sm:py-20"> <section className="py-16 sm:py-20">
<Container className="max-w-3xl"> <Container className="max-w-3xl">
@@ -56,32 +76,35 @@ export default async function BlogDetailPage({
</Link> </Link>
<div className="mt-6 flex items-center gap-3 text-sm text-foreground/70"> <div className="mt-6 flex items-center gap-3 text-sm text-foreground/70">
<span className="rounded-full bg-primary/5 px-3 py-1 text-xs font-semibold text-primary"> {post.categoryNameFa && (
{post.category} <span className="rounded-full bg-primary/5 px-3 py-1 text-xs font-semibold text-primary">
</span> {post.categoryNameFa}
<span>{post.date}</span> </span>
)}
<span>{postDate(post)}</span>
<span className="h-1 w-1 rounded-full bg-foreground/30" /> <span className="h-1 w-1 rounded-full bg-foreground/30" />
<span>{post.readingTime}</span> <span>{postReadingTime(post)}</span>
</div> </div>
<div className="relative mt-6 aspect-[16/9] rounded-3xl overflow-hidden shadow-xl bg-surface"> {image && (
<Image <div className="relative mt-6 aspect-[16/9] rounded-3xl overflow-hidden shadow-xl bg-surface">
src={post.cover} <Image
alt={post.title} src={image}
fill alt={title}
priority fill
className="object-cover" priority
sizes="(min-width: 1024px) 768px, 100vw" className="object-cover"
sizes="(min-width: 1024px) 768px, 100vw"
/>
</div>
)}
{content && (
<div
className="rich-content mt-8"
dangerouslySetInnerHTML={{ __html: content }}
/> />
</div> )}
<div className="mt-8 space-y-5">
{post.content.map((paragraph) => (
<p key={paragraph} className="leading-8 text-foreground">
{paragraph}
</p>
))}
</div>
<div className="mt-10 rounded-2xl bg-surface p-6 flex flex-wrap items-center justify-between gap-4"> <div className="mt-10 rounded-2xl bg-surface p-6 flex flex-wrap items-center justify-between gap-4">
<div> <div>
+28 -15
View File
@@ -3,7 +3,7 @@ import Container from "@/components/Container";
import PageBanner from "@/components/PageBanner"; import PageBanner from "@/components/PageBanner";
import PostCard from "@/components/PostCard"; import PostCard from "@/components/PostCard";
import Pagination from "@/components/Pagination"; import Pagination from "@/components/Pagination";
import { posts } from "@/data/posts"; import { getPosts } from "@/lib/meshkee";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "مقالات | آراد آریسمان", title: "مقالات | آراد آریسمان",
@@ -19,10 +19,15 @@ export default async function BlogPage({
searchParams: Promise<{ page?: string }>; searchParams: Promise<{ page?: string }>;
}) { }) {
const { page } = await searchParams; const { page } = await searchParams;
const totalPages = Math.max(1, Math.ceil(posts.length / PAGE_SIZE)); const requestedPage = Math.max(1, Number(page) || 1);
const currentPage = Math.min(Math.max(1, Number(page) || 1), totalPages);
const start = (currentPage - 1) * PAGE_SIZE; const { items, total } = await getPosts({
const pageItems = posts.slice(start, start + PAGE_SIZE); page: requestedPage,
pageSize: PAGE_SIZE,
});
const totalPages = Math.max(1, Math.ceil(total / PAGE_SIZE));
const currentPage = Math.min(requestedPage, totalPages);
return ( return (
<> <>
@@ -34,17 +39,25 @@ export default async function BlogPage({
<section className="py-16 sm:py-20"> <section className="py-16 sm:py-20">
<Container> <Container>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6"> {items.length > 0 ? (
{pageItems.map((post) => ( <>
<PostCard key={post.id} post={post} /> <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
))} {items.map((post) => (
</div> <PostCard key={post.id} post={post} />
))}
</div>
<Pagination <Pagination
currentPage={currentPage} currentPage={currentPage}
totalPages={totalPages} totalPages={totalPages}
basePath="/blog" basePath="/blog"
/> />
</>
) : (
<p className="text-center text-foreground py-10">
بهزودی مقالات آموزشی جدیدی منتشر میشود.
</p>
)}
</Container> </Container>
</section> </section>
</> </>
+42
View File
@@ -26,3 +26,45 @@ body {
color: var(--foreground); color: var(--foreground);
font-family: var(--font-vazirmatn), Tahoma, sans-serif; font-family: var(--font-vazirmatn), Tahoma, sans-serif;
} }
.rich-content {
color: var(--foreground);
line-height: 2;
}
.rich-content p {
margin-bottom: 1rem;
}
.rich-content table {
width: 100% !important;
height: auto !important;
border-collapse: collapse;
margin: 1.5rem 0;
font-size: 0.9rem;
overflow: hidden;
border-radius: 0.75rem;
}
.rich-content table,
.rich-content th,
.rich-content td {
border: 1px solid rgba(0, 0, 0, 0.08) !important;
}
.rich-content th,
.rich-content td {
padding: 0.6rem 0.75rem;
text-align: center !important;
height: auto !important;
}
.rich-content tr:first-child {
background: var(--surface);
font-weight: 700;
color: var(--secondary);
}
.rich-content strong {
color: var(--secondary);
}
+61 -73
View File
@@ -6,13 +6,18 @@ import Container from "@/components/Container";
import PageBanner from "@/components/PageBanner"; import PageBanner from "@/components/PageBanner";
import ProductCard from "@/components/ProductCard"; import ProductCard from "@/components/ProductCard";
import { import {
getProductBySlug, getProduct,
getRelatedProducts, getProducts,
products, productImage,
} from "@/data/products"; productPriceLabel,
stripHtml,
} from "@/lib/meshkee";
export function generateStaticParams() { export const revalidate = 300;
return products.map((p) => ({ slug: p.slug }));
export async function generateStaticParams() {
const { items } = await getProducts({ page: 1, pageSize: 100 });
return items.map((p) => ({ slug: p.slug }));
} }
export async function generateMetadata({ export async function generateMetadata({
@@ -21,11 +26,11 @@ export async function generateMetadata({
params: Promise<{ slug: string }>; params: Promise<{ slug: string }>;
}): Promise<Metadata> { }): Promise<Metadata> {
const { slug } = await params; const { slug } = await params;
const product = getProductBySlug(slug); const product = await getProduct(slug);
if (!product) return {}; if (!product) return {};
return { return {
title: `${product.title} | آراد آریسمان`, title: `${product.nameFa} | آراد آریسمان`,
description: product.shortDesc, description: product.summary || stripHtml(product.descriptionHtml, 160),
}; };
} }
@@ -35,18 +40,21 @@ export default async function ProductDetailPage({
params: Promise<{ slug: string }>; params: Promise<{ slug: string }>;
}) { }) {
const { slug } = await params; const { slug } = await params;
const product = getProductBySlug(slug); const product = await getProduct(slug);
if (!product) notFound(); if (!product) notFound();
const related = getRelatedProducts(product); const image = productImage(product);
const shortDesc = product.summary || stripHtml(product.descriptionHtml, 160);
const related = product.categoryId
? (await getProducts({ categoryId: product.categoryId, pageSize: 4 })).items
.filter((p) => p.id !== product.id)
.slice(0, 3)
: [];
return ( return (
<> <>
<PageBanner <PageBanner title={product.nameFa} subtitle={shortDesc} breadcrumb={product.nameFa} />
title={product.title}
subtitle={product.shortDesc}
breadcrumb={product.title}
/>
<section className="py-16 sm:py-20"> <section className="py-16 sm:py-20">
<Container> <Container>
@@ -65,34 +73,44 @@ export default async function ProductDetailPage({
<div className="mt-8 grid grid-cols-1 lg:grid-cols-2 gap-12"> <div className="mt-8 grid grid-cols-1 lg:grid-cols-2 gap-12">
<div className="relative aspect-square rounded-3xl overflow-hidden shadow-xl bg-surface"> <div className="relative aspect-square rounded-3xl overflow-hidden shadow-xl bg-surface">
<Image {image ? (
src={product.image} <Image
alt={product.title} src={image}
fill alt={product.nameFa}
priority fill
className="object-cover" priority
sizes="(min-width: 1024px) 45vw, 90vw" className="object-cover"
/> sizes="(min-width: 1024px) 45vw, 90vw"
<span className="absolute top-4 right-4 rounded-full bg-white/90 backdrop-blur px-4 py-1.5 text-sm font-semibold text-primary"> />
{product.category} ) : (
</span> <div className="flex h-full items-center justify-center text-primary/20 text-6xl font-bold">
آ
</div>
)}
{product.categoryNameFa && (
<span className="absolute top-4 right-4 rounded-full bg-white/90 backdrop-blur px-4 py-1.5 text-sm font-semibold text-primary">
{product.categoryNameFa}
</span>
)}
</div> </div>
<div> <div>
<h1 className="text-2xl sm:text-3xl font-extrabold text-primary"> <h1 className="text-2xl sm:text-3xl font-extrabold text-primary">
{product.title} {product.nameFa}
</h1> </h1>
<p className="mt-3 text-foreground leading-8"> {shortDesc && (
{product.shortDesc} <p className="mt-3 text-foreground leading-8">{shortDesc}</p>
</p> )}
<div className="mt-6 flex items-center gap-3"> <div className="mt-6 flex items-center gap-3">
<span className="text-xl font-extrabold text-primary"> <span className="text-xl font-extrabold text-primary">
تماس بگیرید {productPriceLabel(product)}
</span>
<span className="text-sm text-foreground/60">
({product.packaging})
</span> </span>
{!product.store.inStock && (
<span className="text-sm text-foreground/60">
(استعلام موجودی)
</span>
)}
</div> </div>
<div className="mt-6 flex flex-wrap gap-3"> <div className="mt-6 flex flex-wrap gap-3">
@@ -110,48 +128,18 @@ export default async function ProductDetailPage({
استعلام قیمت استعلام قیمت
</Link> </Link>
</div> </div>
<div className="mt-8 rounded-2xl border border-black/5 bg-white p-6">
<h2 className="font-bold text-secondary">نحوه مصرف</h2>
<p className="mt-2 text-sm leading-7 text-foreground">
{product.usage}
</p>
</div>
<div className="mt-6">
<h2 className="font-bold text-secondary">مزایای محصول</h2>
<ul className="mt-3 space-y-2">
{product.benefits.map((b) => (
<li key={b} className="flex items-start gap-2 text-sm text-foreground">
<span className="mt-1 flex h-4 w-4 shrink-0 items-center justify-center rounded-full bg-primary/10 text-primary">
<svg width="10" height="10" viewBox="0 0 24 24" fill="none">
<path
d="M5 13l4 4L19 7"
stroke="currentColor"
strokeWidth="3"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</span>
{b}
</li>
))}
</ul>
</div>
</div> </div>
</div> </div>
<div className="mt-14 rounded-2xl border border-black/5 bg-white p-6 sm:p-8"> {product.descriptionHtml && (
<h2 className="text-lg font-bold text-secondary">معرفی محصول</h2> <div className="mt-14 rounded-2xl border border-black/5 bg-white p-6 sm:p-8">
<div className="mt-4 space-y-4"> <h2 className="text-lg font-bold text-secondary">معرفی محصول</h2>
{product.description.map((paragraph) => ( <div
<p key={paragraph} className="leading-8 text-foreground"> className="rich-content mt-4"
{paragraph} dangerouslySetInnerHTML={{ __html: product.descriptionHtml }}
</p> />
))}
</div> </div>
</div> )}
</Container> </Container>
</section> </section>
+28 -18
View File
@@ -3,7 +3,7 @@ import Container from "@/components/Container";
import PageBanner from "@/components/PageBanner"; import PageBanner from "@/components/PageBanner";
import ProductCard from "@/components/ProductCard"; import ProductCard from "@/components/ProductCard";
import Pagination from "@/components/Pagination"; import Pagination from "@/components/Pagination";
import { products } from "@/data/products"; import { getProducts } from "@/lib/meshkee";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "محصولات | آراد آریسمان", title: "محصولات | آراد آریسمان",
@@ -19,13 +19,15 @@ export default async function ProductsPage({
searchParams: Promise<{ page?: string }>; searchParams: Promise<{ page?: string }>;
}) { }) {
const { page } = await searchParams; const { page } = await searchParams;
const totalPages = Math.max(1, Math.ceil(products.length / PAGE_SIZE)); const requestedPage = Math.max(1, Number(page) || 1);
const currentPage = Math.min(
Math.max(1, Number(page) || 1), const { items, total } = await getProducts({
totalPages page: requestedPage,
); pageSize: PAGE_SIZE,
const start = (currentPage - 1) * PAGE_SIZE; });
const pageItems = products.slice(start, start + PAGE_SIZE);
const totalPages = Math.max(1, Math.ceil(total / PAGE_SIZE));
const currentPage = Math.min(requestedPage, totalPages);
return ( return (
<> <>
@@ -37,17 +39,25 @@ export default async function ProductsPage({
<section className="py-16 sm:py-20"> <section className="py-16 sm:py-20">
<Container> <Container>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6"> {items.length > 0 ? (
{pageItems.map((product) => ( <>
<ProductCard key={product.id} product={product} /> <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
))} {items.map((product) => (
</div> <ProductCard key={product.id} product={product} />
))}
</div>
<Pagination <Pagination
currentPage={currentPage} currentPage={currentPage}
totalPages={totalPages} totalPages={totalPages}
basePath="/products" basePath="/products"
/> />
</>
) : (
<p className="text-center text-foreground py-10">
در حال حاضر محصولی برای نمایش وجود ندارد.
</p>
)}
</Container> </Container>
</section> </section>
</> </>
+38 -17
View File
@@ -1,37 +1,58 @@
import Image from "next/image"; import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
import type { Post } from "@/data/posts"; import type { Post } from "@/lib/meshkee";
import {
postDate,
postExcerpt,
postImage,
postReadingTime,
postTitle,
} from "@/lib/meshkee";
export default function PostCard({ post }: { post: Post }) { export default function PostCard({ post }: { post: Post }) {
const title = postTitle(post);
const image = postImage(post);
const excerpt = postExcerpt(post, 90);
return ( return (
<Link <Link
href={`/blog/${post.slug}`} href={`/blog/${post.slug}`}
className="group flex flex-col rounded-2xl border border-black/5 bg-white overflow-hidden shadow-sm hover:shadow-lg transition-shadow" className="group flex flex-col rounded-2xl border border-black/5 bg-white overflow-hidden shadow-sm hover:shadow-lg transition-shadow"
> >
<div className="relative aspect-[16/10] bg-surface"> <div className="relative aspect-[16/10] bg-surface">
<Image {image ? (
src={post.cover} <Image
alt={post.title} src={image}
fill alt={title}
className="object-cover group-hover:scale-105 transition-transform duration-500" fill
sizes="(min-width: 1024px) 33vw, (min-width: 640px) 50vw, 100vw" className="object-cover group-hover:scale-105 transition-transform duration-500"
/> sizes="(min-width: 1024px) 33vw, (min-width: 640px) 50vw, 100vw"
<span className="absolute top-3 right-3 rounded-full bg-white/90 backdrop-blur px-3 py-1 text-xs font-semibold text-primary"> />
{post.category} ) : (
</span> <div className="flex h-full items-center justify-center text-primary/20 text-4xl font-bold">
آ
</div>
)}
{post.categoryNameFa && (
<span className="absolute top-3 right-3 rounded-full bg-white/90 backdrop-blur px-3 py-1 text-xs font-semibold text-primary">
{post.categoryNameFa}
</span>
)}
</div> </div>
<div className="flex flex-1 flex-col p-5"> <div className="flex flex-1 flex-col p-5">
<div className="flex items-center gap-3 text-xs text-foreground/70"> <div className="flex items-center gap-3 text-xs text-foreground/70">
<span>{post.date}</span> <span>{postDate(post)}</span>
<span className="h-1 w-1 rounded-full bg-foreground/30" /> <span className="h-1 w-1 rounded-full bg-foreground/30" />
<span>{post.readingTime}</span> <span>{postReadingTime(post)}</span>
</div> </div>
<h3 className="mt-3 font-bold text-secondary leading-7 line-clamp-2"> <h3 className="mt-3 font-bold text-secondary leading-7 line-clamp-2">
{post.title} {title}
</h3> </h3>
<p className="mt-2 text-sm leading-6 text-foreground line-clamp-2 flex-1"> {excerpt && (
{post.excerpt} <p className="mt-2 text-sm leading-6 text-foreground line-clamp-2 flex-1">
</p> {excerpt}
</p>
)}
<span className="mt-4 inline-flex items-center gap-2 text-sm font-semibold text-primary"> <span className="mt-4 inline-flex items-center gap-2 text-sm font-semibold text-primary">
ادامه مطلب ادامه مطلب
<svg <svg
+30 -16
View File
@@ -1,34 +1,48 @@
import Image from "next/image"; import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
import type { Product } from "@/data/products"; import type { Product } from "@/lib/meshkee";
import { productImage, productPriceLabel, stripHtml } from "@/lib/meshkee";
export default function ProductCard({ product }: { product: Product }) { export default function ProductCard({ product }: { product: Product }) {
const image = productImage(product);
const excerpt = product.summary || stripHtml(product.descriptionHtml, 90);
return ( return (
<Link <Link
href={`/products/${product.slug}`} href={`/products/${product.slug}`}
className="group rounded-2xl border border-black/5 bg-white overflow-hidden shadow-sm hover:shadow-lg transition-shadow" className="group rounded-2xl border border-black/5 bg-white overflow-hidden shadow-sm hover:shadow-lg transition-shadow"
> >
<div className="relative aspect-square bg-surface"> <div className="relative aspect-square bg-surface">
<Image {image ? (
src={product.image} <Image
alt={product.title} src={image}
fill alt={product.nameFa}
className="object-cover group-hover:scale-105 transition-transform duration-500" fill
sizes="(min-width: 1024px) 25vw, (min-width: 640px) 33vw, 50vw" className="object-cover group-hover:scale-105 transition-transform duration-500"
/> sizes="(min-width: 1024px) 25vw, (min-width: 640px) 33vw, 50vw"
<span className="absolute top-3 right-3 rounded-full bg-white/90 backdrop-blur px-3 py-1 text-xs font-semibold text-primary"> />
{product.category} ) : (
</span> <div className="flex h-full items-center justify-center text-primary/20 text-4xl font-bold">
آ
</div>
)}
{product.categoryNameFa && (
<span className="absolute top-3 right-3 rounded-full bg-white/90 backdrop-blur px-3 py-1 text-xs font-semibold text-primary">
{product.categoryNameFa}
</span>
)}
</div> </div>
<div className="p-4"> <div className="p-4">
<h3 className="font-semibold text-secondary leading-6 line-clamp-2 min-h-12"> <h3 className="font-semibold text-secondary leading-6 line-clamp-2 min-h-12">
{product.title} {product.nameFa}
</h3> </h3>
<p className="mt-1 text-sm text-foreground line-clamp-2"> {excerpt && (
{product.shortDesc} <p className="mt-1 text-sm text-foreground line-clamp-2">{excerpt}</p>
</p> )}
<div className="mt-3 flex items-center justify-between"> <div className="mt-3 flex items-center justify-between">
<span className="text-sm font-bold text-primary">تماس بگیرید</span> <span className="text-sm font-bold text-primary">
{productPriceLabel(product)}
</span>
<span className="flex h-8 w-8 items-center justify-center rounded-full bg-primary/5 text-primary rotate-180 group-hover:bg-primary group-hover:text-white transition-colors"> <span className="flex h-8 w-8 items-center justify-center rounded-full bg-primary/5 text-primary rotate-180 group-hover:bg-primary group-hover:text-white transition-colors">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none"> <svg width="14" height="14" viewBox="0 0 24 24" fill="none">
<path <path
+5 -3
View File
@@ -1,10 +1,12 @@
import Link from "next/link"; import Link from "next/link";
import Container from "./Container"; import Container from "./Container";
import ProductCard from "./ProductCard"; import ProductCard from "./ProductCard";
import { products } from "@/data/products"; import { getProducts } from "@/lib/meshkee";
export default function Products() { export default async function Products() {
const featured = products.slice(0, 8); const { items: featured } = await getProducts({ page: 1, pageSize: 8 });
if (featured.length === 0) return null;
return ( return (
<section id="products" className="py-16 sm:py-20"> <section id="products" className="py-16 sm:py-20">
-211
View File
@@ -1,211 +0,0 @@
export type Post = {
id: number;
slug: string;
title: string;
excerpt: string;
cover: string;
category: string;
date: string;
readingTime: string;
content: string[];
};
export const posts: Post[] = [
{
id: 1,
slug: "increase-egg-production-tips",
title: "۵ نکته کلیدی برای افزایش بازده تخم‌گذاری مرغ‌های تخمگذار",
excerpt:
"بازده تخم‌گذاری به عوامل متعددی از جمله تغذیه، نور و مدیریت گله بستگی دارد. در این مقاله به مهم‌ترین نکاتی که می‌توانند بازده گله شما را بهبود دهند می‌پردازیم.",
cover: "/images/product-1.jpg",
category: "طیور",
date: "۱۲ اردیبهشت ۱۴۰۴",
readingTime: "۵ دقیقه مطالعه",
content: [
"تولید تخم‌مرغ با کیفیت و پایدار، حاصل ترکیبی از تغذیه صحیح، مدیریت محیط پرورش و سلامت عمومی گله است. در ادامه به پنج نکته کلیدی برای بهبود بازده تخم‌گذاری اشاره می‌کنیم.",
"۱. تعادل جیره غذایی: جیره مرغ تخمگذار باید ترکیبی متعادل از انرژی، پروتئین، کلسیم و فسفر باشد. کمبود یا مازاد هر یک از این اجزا می‌تواند مستقیما بر کیفیت پوسته و میزان تولید اثر بگذارد.",
"۲. تامین کلسیم کافی: با نزدیک‌شدن به اوج تولید، نیاز مرغ به کلسیم افزایش می‌یابد. استفاده از مکمل‌های معدنی استاندارد به حفظ استحکام پوسته و کاهش تخم‌های شکسته کمک می‌کند.",
"۳. مدیریت نور: برنامه نوری منظم و پایدار نقش مهمی در تحریک و حفظ چرخه تولید دارد. تغییرات ناگهانی در ساعات روشنایی می‌تواند باعث افت موقت تولید شود.",
"۴. کنترل استرس محیطی: دمای نامناسب، تراکم بالا و جابجایی‌های مکرر از عوامل استرس‌زا هستند که بر بازده تولید تاثیر منفی می‌گذارند.",
"۵. استفاده از مکمل‌های ویتامینه: مکمل‌های ویتامینه با تامین ویتامین‌های ضروری، به حفظ سلامت عمومی گله و پایداری تولید در بلندمدت کمک می‌کنند.",
"رعایت این نکات به همراه مشورت مستمر با کارشناسان تغذیه دام و طیور، می‌تواند بازده اقتصادی واحدهای پرورش مرغ تخمگذار را به‌طور محسوسی افزایش دهد.",
],
},
{
id: 2,
slug: "increase-calf-growth-rate",
title: "چگونه رشد گوساله‌های پرواری را افزایش دهیم؟",
excerpt:
"دستیابی به وزن هدف در کوتاه‌ترین زمان ممکن، هدف اصلی هر واحد پرواربندی است. در این مطلب راهکارهای عملی برای افزایش رشد گوساله‌های پرواری را بررسی می‌کنیم.",
cover: "/images/stats-banner.jpg",
category: "دام سنگین",
date: "۳ خرداد ۱۴۰۴",
readingTime: "۶ دقیقه مطالعه",
content: [
"افزایش وزن‌گیری روزانه و بهبود بازده تبدیل خوراک، دو شاخص کلیدی موفقیت در واحدهای پرواربندی گوساله هستند. دستیابی به این اهداف نیازمند برنامه‌ریزی دقیق تغذیه‌ای و مدیریتی است.",
"شروع دوره پروار با گوساله‌های سالم و عاری از استرس انتقال، پایه‌ای برای موفقیت دوره است. دوره تطبیق اولیه باید با دقت و بدون تغییرات ناگهانی در جیره انجام شود.",
"افزایش تدریجی سهم کنسانتره در جیره، همراه با تامین فیبر کافی از منابعی مانند سبوس گندم، به حفظ سلامت شکمبه و جلوگیری از اسیدوز کمک می‌کند.",
"استفاده از مکمل‌های معدنی و ویتامینه استاندارد، نقش مهمی در پیشگیری از اختلالات متابولیک و حفظ اشتها در طول دوره پروار دارد.",
"پایش منظم وزن گله در بازه‌های زمانی مشخص، امکان ارزیابی روند رشد و اصلاح به‌موقع برنامه تغذیه‌ای را فراهم می‌کند.",
"در نهایت، همکاری نزدیک با کارشناس تغذیه دام برای تنظیم جیره متناسب با نژاد، سن و وزن گوساله‌ها، بهترین نتیجه اقتصادی را در پی خواهد داشت.",
],
},
{
id: 3,
slug: "vitamin-supplements-poultry-importance",
title: "اهمیت مکمل‌های ویتامینه در تغذیه طیور",
excerpt:
"ویتامین‌ها اگرچه در مقادیر کم مورد نیاز هستند، اما نقشی حیاتی در سلامت، رشد و تولید طیور ایفا می‌کنند. در این مقاله به نقش مکمل‌های ویتامینه می‌پردازیم.",
cover: "/images/product-3.jpg",
category: "طیور",
date: "۱۸ خرداد ۱۴۰۴",
readingTime: "۴ دقیقه مطالعه",
content: [
"ویتامین‌ها گروهی از ترکیبات آلی هستند که بدن طیور به مقدار کم به آن‌ها نیاز دارد، اما نقش آن‌ها در فرآیندهای حیاتی بسیار پررنگ است.",
"ویتامین‌های محلول در چربی مانند A، D3، E و K در عملکردهایی مانند بینایی، جذب کلسیم، سیستم ایمنی و انعقاد خون نقش دارند. ویتامین‌های گروه B نیز در متابولیسم انرژی و رشد سلولی موثرند.",
"در سیستم‌های پرورش صنعتی، طیور معمولا دسترسی محدودی به منابع طبیعی ویتامین دارند؛ به همین دلیل تامین این ترکیبات از طریق مکمل‌های استاندارد ضروری است.",
"کمبود ویتامین می‌تواند خود را به شکل کاهش رشد، افت تولید، ضعف سیستم ایمنی و افزایش حساسیت به بیماری‌ها نشان دهد.",
"استفاده منظم و متناسب با سن و مرحله تولید از مکمل‌های ویتامینه استاندارد، یکی از موثرترین و مقرون‌به‌صرفه‌ترین راهکارها برای حفظ سلامت و بازده گله است.",
],
},
{
id: 4,
slug: "choosing-right-sheep-concentrate",
title: "راهنمای انتخاب کنسانتره مناسب برای گوسفند",
excerpt:
"انتخاب کنسانتره مناسب باید بر اساس هدف پرورش، سن و وضعیت فیزیولوژیک گله انجام شود. در این مطلب معیارهای انتخاب کنسانتره گوسفند را بررسی می‌کنیم.",
cover: "/images/product-5.jpg",
category: "گوسفند",
date: "۲ تیر ۱۴۰۴",
readingTime: "۵ دقیقه مطالعه",
content: [
"کنسانتره‌های موجود در بازار از نظر ترکیب انرژی، پروتئین و ریزمغذی‌ها با یکدیگر متفاوت هستند و انتخاب نادرست می‌تواند بر بازده اقتصادی پرورش تاثیر منفی بگذارد.",
"برای گله‌های پرواری، کنسانتره باید انرژی بالایی داشته باشد تا وزن‌گیری روزانه در کوتاه‌ترین زمان ممکن حاصل شود. در مقابل، برای میش‌های مولد، تعادل پروتئین و مواد معدنی اهمیت بیشتری دارد.",
"توجه به مرحله فیزیولوژیک گله (نگهداری، آبستنی، شیردهی یا پروار) در انتخاب فرمول کنسانتره ضروری است، زیرا نیاز غذایی در هر مرحله متفاوت است.",
"کیفیت مواد اولیه و ثبات ترکیبات کنسانتره از بچی به بچ دیگر، از عوامل مهم در انتخاب تامین‌کننده معتبر است.",
"مشورت با کارشناس تغذیه دام برای تنظیم دقیق جیره بر اساس شرایط منطقه، نژاد گله و اهداف تولیدی، بهترین نتیجه را در پی خواهد داشت.",
],
},
{
id: 5,
slug: "mineral-supplements-role-livestock-health",
title: "نقش مکمل‌های معدنی در سلامت دام",
excerpt:
"عناصر معدنی اگرچه درصد کمی از وزن بدن دام را تشکیل می‌دهند، اما نقشی اساسی در سلامت، رشد و تولیدمثل دارند. در این مقاله به این نقش می‌پردازیم.",
cover: "/images/product-4.jpg",
category: "دام سنگین",
date: "۱۵ تیر ۱۴۰۴",
readingTime: "۴ دقیقه مطالعه",
content: [
"عناصر معدنی به دو دسته کلی ماکرومعدنی‌ها (مانند کلسیم، فسفر، سدیم) و ریزمغذی‌های معدنی (مانند روی، مس و منگنز) تقسیم می‌شوند که هر یک نقش مشخصی در بدن دام ایفا می‌کنند.",
"کلسیم و فسفر در ساختار استخوان و تولید شیر نقش دارند، در حالی که عناصر کمیابی مانند روی و مس در عملکرد سیستم ایمنی و باروری اهمیت ویژه‌ای دارند.",
"کمبود مواد معدنی معمولا به آرامی و بدون علائم واضح بروز می‌کند و می‌تواند در بلندمدت باعث کاهش راندمان تولیدمثل و افت تولید شود.",
"استفاده از مکمل‌های معدنی استاندارد و متناسب با تحلیل خاک و علوفه منطقه، راهکاری موثر برای پیشگیری از این کمبودها است.",
"توصیه می‌شود برنامه مکمل‌دهی معدنی با مشورت کارشناس تغذیه و بر اساس وضعیت خاص هر گله تنظیم شود.",
],
},
{
id: 6,
slug: "winter-feeding-honeybee-colonies",
title: "تغذیه زنبور عسل در فصل زمستان",
excerpt:
"فصل زمستان به دلیل کمبود منابع طبیعی گرده و شهد، یکی از حساس‌ترین دوره‌ها برای بقای کلنی زنبور عسل است. در این مطلب به نکات تغذیه زمستانه می‌پردازیم.",
cover: "/images/category-8.jpg",
category: "زنبور عسل",
date: "۸ مرداد ۱۴۰۴",
readingTime: "۵ دقیقه مطالعه",
content: [
"با کاهش دما و کمبود گل در فصل زمستان، کلنی‌های زنبور عسل با محدودیت شدید منابع طبیعی تغذیه مواجه می‌شوند و نیاز به تغذیه کمکی پیدا می‌کنند.",
"ذخیره‌سازی عسل کافی در کندو پیش از شروع سرما، اولین و مهم‌ترین اقدام برای عبور موفق کلنی از زمستان است.",
"در صورت کمبود ذخیره طبیعی، استفاده از مکمل‌های تغذیه‌ای مخصوص زنبور عسل که ترکیبی از قند و پروتئین هستند، می‌تواند از گرسنگی کلنی جلوگیری کند.",
"کنترل رطوبت داخل کندو و جلوگیری از نفوذ سرمای شدید نیز به همان اندازه تغذیه، در بقای کلنی موثر است.",
"بازدید دوره‌ای و محدود از کندوها در روزهای نسبتا گرم زمستان، امکان ارزیابی وضعیت ذخیره غذایی و انجام اقدامات لازم را فراهم می‌کند.",
],
},
{
id: 7,
slug: "why-processed-rice-bran",
title: "سبوس برنج فرآوری شده؛ چرا و چگونه؟",
excerpt:
"سبوس برنج خام به دلیل فعالیت آنزیمی بالا مستعد تندی سریع است. فرآیند پایدارسازی این مشکل را برطرف کرده و آن را به منبعی ارزشمند در تغذیه دام تبدیل می‌کند.",
cover: "/images/product-6.jpg",
category: "خوراک دام",
date: "۲۰ مرداد ۱۴۰۴",
readingTime: "۴ دقیقه مطالعه",
content: [
"سبوس برنج یکی از فرآورده‌های جانبی صنعت شالی‌کوبی است که به دلیل ترکیب غنی از انرژی، فیبر و اسیدهای چرب، پتانسیل بالایی در تغذیه دام و طیور دارد.",
"با این حال، سبوس خام حاوی آنزیم لیپاز فعال است که به سرعت باعث تجزیه چربی و تندشدن آن می‌شود؛ این موضوع کیفیت و خوش‌خوراکی محصول را کاهش می‌دهد.",
"فرآیند پایدارسازی حرارتی، این آنزیم را غیرفعال کرده و ماندگاری، کیفیت تغذیه‌ای و خوش‌خوراکی سبوس را به طور قابل توجهی افزایش می‌دهد.",
"سبوس برنج فرآوری‌شده به دلیل قیمت مناسب و ارزش تغذیه‌ای بالا، گزینه‌ای اقتصادی برای گنجاندن در جیره دام و طیور محسوب می‌شود.",
"توجه به منبع تامین و فرآیند تولید، در انتخاب سبوس برنج با کیفیت باثبات اهمیت زیادی دارد.",
],
},
{
id: 8,
slug: "vitamin-d3-deficiency-poultry",
title: "علائم کمبود ویتامین D3 در طیور و راه‌حل آن",
excerpt:
"ویتامین D3 نقشی کلیدی در جذب کلسیم و سلامت استخوان طیور دارد. در این مقاله علائم کمبود آن و راهکارهای پیشگیری را بررسی می‌کنیم.",
cover: "/images/product-11.jpg",
category: "طیور",
date: "۵ شهریور ۱۴۰۴",
readingTime: "۵ دقیقه مطالعه",
content: [
"ویتامین D3 با تسهیل جذب کلسیم و فسفر از روده، نقش تعیین‌کننده‌ای در استحکام استخوان و کیفیت پوسته تخم‌مرغ ایفا می‌کند.",
"از جمله علائم قابل مشاهده کمبود این ویتامین می‌توان به ضعف پا، کاهش استحکام پوسته تخم‌مرغ، افت تولید و کندی رشد در جوجه‌ها اشاره کرد.",
"از آنجا که طیور پرورش صنعتی معمولا دسترسی محدودی به نور مستقیم خورشید دارند، تامین ویتامین D3 عمدتا باید از طریق جیره غذایی صورت گیرد.",
"استفاده از پرمیکس‌های استاندارد حاوی ویتامین D3 با غلظت مناسب، ساده‌ترین و موثرترین راه پیشگیری از این کمبود است.",
"در صورت مشاهده علائم مشکوک، مشورت با دامپزشک یا کارشناس تغذیه برای بررسی دقیق‌تر و اصلاح جیره توصیه می‌شود.",
],
},
{
id: 9,
slug: "feed-supplement-storage-best-practices",
title: "بهترین شیوه‌های نگهداری مکمل‌های خوراک دام",
excerpt:
"نگهداری نامناسب می‌تواند کیفیت و اثربخشی مکمل‌های غذایی را به شدت کاهش دهد. در این مطلب اصول صحیح نگهداری این محصولات را مرور می‌کنیم.",
cover: "/images/category-3.jpg",
category: "خوراک دام",
date: "۱۲ شهریور ۱۴۰۴",
readingTime: "۳ دقیقه مطالعه",
content: [
"بسیاری از ویتامین‌ها و ترکیبات فعال موجود در مکمل‌های غذایی نسبت به رطوبت، حرارت و نور حساس هستند و نگهداری نادرست می‌تواند اثربخشی آن‌ها را کاهش دهد.",
"مکمل‌ها باید در محیطی خشک، خنک و دور از تابش مستقیم نور خورشید نگهداری شوند. رطوبت بالا می‌تواند باعث کلوخه‌شدن و رشد کپک در محصول شود.",
"کیسه‌های باز‌شده باید به خوبی بسته و در اسرع وقت مصرف شوند تا از افت کیفیت جلوگیری شود.",
"رعایت اصل «اول وارد، اول مصرف» (FIFO) در انبارداری، به اطمینان از مصرف محصولات در تاریخ مناسب کمک می‌کند.",
"چیدمان کیسه‌ها بر روی پالت و دور از تماس مستقیم با کف و دیوار انبار، از رطوبت‌گیری و آسیب احتمالی جلوگیری می‌کند.",
],
},
{
id: 10,
slug: "nutrition-impact-on-meat-egg-quality",
title: "تاثیر تغذیه صحیح بر کیفیت گوشت و تخم‌مرغ",
excerpt:
"کیفیت نهایی گوشت و تخم‌مرغ تولیدی، ارتباط مستقیمی با نوع و کیفیت جیره مصرفی دارد. در این مقاله این رابطه را بررسی می‌کنیم.",
cover: "/images/hero-2.jpg",
category: "طیور",
date: "۲۸ شهریور ۱۴۰۴",
readingTime: "۴ دقیقه مطالعه",
content: [
"ترکیب جیره غذایی طیور، تاثیر مستقیمی بر رنگ، طعم و ارزش تغذیه‌ای محصول نهایی اعم از گوشت و تخم‌مرغ دارد.",
"منابع پروتئینی و اسیدهای چرب موجود در جیره، بر بافت و طعم گوشت اثرگذارند، در حالی که رنگدانه‌های طبیعی موجود در برخی مکمل‌ها، رنگ زرده تخم‌مرغ را بهبود می‌بخشند.",
"مکمل‌های معدنی و ویتامینه با کیفیت، علاوه بر افزایش راندمان تولید، به بهبود شاخص‌های کیفی محصول نهایی نیز کمک می‌کنند.",
"توجه به کیفیت جیره از ابتدای دوره پرورش، نه‌تنها بر عملکرد گله بلکه بر رضایت مصرف‌کننده نهایی محصولات دامی نیز تاثیرگذار است.",
"سرمایه‌گذاری در تغذیه با کیفیت، در نهایت هم به نفع تولیدکننده و هم مصرف‌کننده تمام می‌شود.",
],
},
];
export function getPostBySlug(slug: string) {
return posts.find((p) => p.slug === slug);
}
export function getRelatedPosts(post: Post, limit = 3) {
return posts
.filter((p) => p.id !== post.id && p.category === post.category)
.slice(0, limit)
.concat(
posts.filter((p) => p.id !== post.id && p.category !== post.category)
)
.slice(0, limit);
}
-325
View File
@@ -1,325 +0,0 @@
export type Product = {
id: number;
slug: string;
title: string;
category: string;
image: string;
shortDesc: string;
description: string[];
packaging: string;
usage: string;
benefits: string[];
};
export const products: Product[] = [
{
id: 1,
slug: "mineral-supplement-laying-hen",
title: "مکمل معدنی مرغ تخمگذار",
category: "مرغ تخمگذار",
image: "/images/product-1.jpg",
shortDesc: "تامین‌کننده عناصر معدنی ضروری برای افزایش کیفیت و دوام پوسته تخم‌مرغ",
description: [
"مکمل معدنی مرغ تخمگذار آراد آریسمان با فرمولاسیونی متعادل از عناصر ماکرو و میکرو معدنی، نیاز روزانه مرغ‌های تخمگذار را در دوره تولید تامین می‌کند.",
"استفاده منظم از این مکمل به بهبود استحکام پوسته، کاهش تخم‌های شکسته و افزایش یکنواختی وزن تخم‌مرغ کمک می‌کند.",
],
packaging: "کیسه ۲۵ کیلوگرمی",
usage: "طبق دستور کارشناس تغذیه، معمولا به میزان ۱ تا ۲ درصد در جیره روزانه مخلوط می‌شود.",
benefits: [
"افزایش استحکام و کیفیت پوسته تخم‌مرغ",
"کاهش درصد تخم‌های شکسته و بدشکل",
"تامین کلسیم و فسفر قابل جذب",
"بهبود یکنواختی تولید در گله",
],
},
{
id: 2,
slug: "vitamin-supplement-laying-hen",
title: "مکمل ویتامینه مرغ تخمگذار",
category: "مرغ تخمگذار",
image: "/images/product-2.jpg",
shortDesc: "پک کامل ویتامین‌های محلول در آب و چربی برای دوره تولید",
description: [
"این مکمل ترکیبی متعادل از ویتامین‌های A، D3، E، K و گروه B است که برای حفظ سلامت عمومی و راندمان تولید مرغ تخمگذار طراحی شده است.",
"کمبود ویتامین در دوره تولید می‌تواند منجر به افت تخم‌گذاری شود؛ مصرف منظم این مکمل از این افت پیشگیری می‌کند.",
],
packaging: "کیسه ۲۵ کیلوگرمی",
usage: "به صورت مخلوط در خوراک یا محلول در آب آشامیدنی طبق دستور دامپزشک قابل استفاده است.",
benefits: [
"تقویت سیستم ایمنی گله",
"بهبود باروری و کیفیت جوجه‌درآوری",
"افزایش پایداری تولید در فصول گرم و سرد",
"کاهش استرس ناشی از تراکم و جابجایی",
],
},
{
id: 3,
slug: "vitamin-supplement-broiler",
title: "مکمل ویتامینه مرغ گوشتی",
category: "مرغ گوشتی",
image: "/images/product-3.jpg",
shortDesc: "پشتیبان رشد سریع و یکنواخت در دوره پرورش گوشتی",
description: [
"مکمل ویتامینه مرغ گوشتی آراد آریسمان برای پوشش نیاز ویتامینی جوجه‌های گوشتی در تمامی مراحل رشد فرموله شده است.",
"این محصول با حمایت از سیستم ایمنی و بهبود متابولیسم، به افزایش ضریب تبدیل غذایی کمک می‌کند.",
],
packaging: "کیسه ۲۵ کیلوگرمی",
usage: "بر اساس سن و وزن گله و طبق برنامه تغذیه‌ای توصیه‌شده مصرف شود.",
benefits: [
"بهبود ضریب تبدیل غذایی",
"افزایش سرعت و یکنواختی رشد",
"کاهش تلفات دوره پرورش",
"تقویت مقاومت در برابر تنش‌های محیطی",
],
},
{
id: 4,
slug: "mineral-supplement-broiler",
title: "مکمل معدنی مرغ گوشتی",
category: "مرغ گوشتی",
image: "/images/product-4.jpg",
shortDesc: "تامین عناصر معدنی برای استخوان‌بندی و رشد سالم",
description: [
"این مکمل معدنی، ترکیبی غنی از کلسیم، فسفر، سدیم و ریزمغذی‌های ضروری برای رشد اسکلتی سالم جوجه‌های گوشتی است.",
"استخوان‌بندی قوی، پیش‌نیاز دستیابی به وزن هدف در پایان دوره پرورش است.",
],
packaging: "کیسه ۲۵ کیلوگرمی",
usage: "طبق فرمول جیره و با نظر کارشناس تغذیه دام و طیور در خوراک مخلوط شود.",
benefits: [
"تقویت استخوان‌بندی و کاهش لنگش",
"بهبود جذب و بازده خوراک مصرفی",
"کاهش ریسک اختلالات متابولیک",
"پشتیبانی از رشد یکنواخت گله",
],
},
{
id: 5,
slug: "sheep-concentrate",
title: "کنسانتره گوسفند",
category: "گوسفند",
image: "/images/product-5.jpg",
shortDesc: "خوراک متعادل برای افزایش وزن و بازده پرواربندی",
description: [
"کنسانتره گوسفند آراد آریسمان با ترکیبی متوازن از انرژی، پروتئین، ویتامین و مواد معدنی، برای دوره پرواربندی و نگهداری طراحی شده است.",
"این محصول مصرف خوراک را بهینه کرده و بازده اقتصادی پرورش را افزایش می‌دهد.",
],
packaging: "کیسه ۲۵ و ۴۰ کیلوگرمی",
usage: "متناسب با سن، وزن و هدف پرورش (پروار یا نگهداری) در جیره روزانه استفاده شود.",
benefits: [
"افزایش وزن‌گیری روزانه",
"بهبود بازده خوراک مصرفی",
"تامین انرژی و پروتئین متعادل",
"مناسب برای گله‌های پرواری و مولد",
],
},
{
id: 6,
slug: "processed-rice-bran",
title: "سبوس برنج فرآوری شده",
category: "خوراک دام",
image: "/images/product-6.jpg",
shortDesc: "منبع انرژی و فیبر با کیفیت برای جیره دام و طیور",
description: [
"سبوس برنج فرآوری‌شده با فرآیند پایدارسازی، از تندشدن چربی جلوگیری کرده و ماندگاری و کیفیت تغذیه‌ای بالاتری نسبت به سبوس خام دارد.",
"این محصول منبع خوبی از انرژی، فیبر و اسیدهای چرب ضروری برای جیره دام و طیور است.",
],
packaging: "کیسه ۲۵ کیلوگرمی",
usage: "به عنوان بخشی از جیره پایه، طبق فرمولاسیون کارشناس تغذیه در نظر گرفته شود.",
benefits: [
"پایداری بالا در برابر تندی چربی",
"منبع انرژی و فیبر با کیفیت",
"بهبود سلامت دستگاه گوارش",
"مقرون‌به‌صرفه در فرمولاسیون جیره",
],
},
{
id: 7,
slug: "wheat-bran",
title: "سبوس گندم",
category: "خوراک دام",
image: "/images/product-7.jpg",
shortDesc: "منبع فیبر طبیعی برای تنظیم عملکرد گوارشی دام",
description: [
"سبوس گندم به عنوان یک منبع فیبر طبیعی، نقش مهمی در تنظیم عملکرد شکمبه و دستگاه گوارش نشخوارکنندگان دارد.",
"این محصول با کیفیت یکنواخت و عاری از آلودگی، برای مصرف در جیره دام سبک و سنگین مناسب است.",
],
packaging: "کیسه ۲۵ کیلوگرمی",
usage: "به عنوان جزئی از جیره فیبردار، مطابق فرمول تغذیه دام مصرف شود.",
benefits: [
"تنظیم عملکرد شکمبه و گوارش",
"منبع فیبر و انرژی طبیعی",
"کیفیت و یکنواختی بالا",
"سازگار با انواع جیره دام",
],
},
{
id: 8,
slug: "dairy-cattle-supplement",
title: "مکمل دام شیری",
category: "دام سنگین",
image: "/images/product-8.jpg",
shortDesc: "پشتیبان تولید شیر و سلامت گاوهای شیری",
description: [
"مکمل دام شیری با تامین انرژی، پروتئین قابل‌متابولیسم، ویتامین و مواد معدنی، از تولید پایدار شیر و سلامت باروری گاو شیری حمایت می‌کند.",
"فرمولاسیون این محصول با در نظر گرفتن نیاز دوره‌های مختلف شیردهی طراحی شده است.",
],
packaging: "کیسه ۲۵ کیلوگرمی",
usage: "بر اساس مرحله شیردهی و میزان تولید، طبق برنامه تغذیه‌ای در جیره گنجانده شود.",
benefits: [
"افزایش پایدار تولید شیر",
"بهبود درصد چربی و پروتئین شیر",
"پشتیبانی از باروری و سلامت سم",
"کاهش ریسک اختلالات متابولیک دوره زایمان",
],
},
{
id: 9,
slug: "fattening-cattle-supplement",
title: "مکمل دام پرواری",
category: "دام سنگین",
image: "/images/product-9.jpg",
shortDesc: "افزایش وزن‌گیری روزانه در دوره پروار",
description: [
"مکمل دام پرواری آراد آریسمان با تمرکز بر افزایش بازده خوراک مصرفی، به دستیابی سریع‌تر به وزن هدف در پایان دوره پروار کمک می‌کند.",
"این محصول ترکیبی از مواد معدنی، ویتامین و افزودنی‌های بهبود‌دهنده گوارش را در خود دارد.",
],
packaging: "کیسه ۲۵ کیلوگرمی",
usage: "متناسب با وزن اولیه و طول دوره پروار، طبق فرمول جیره مصرف شود.",
benefits: [
"افزایش وزن‌گیری روزانه",
"بهبود بازده تبدیل خوراک",
"کاهش دوره پرواربندی",
"پشتیبانی از سلامت گوارشی دام",
],
},
{
id: 10,
slug: "premix-vitamin-k3",
title: "پرمیکس ویتامین K3",
category: "پرمیکس",
image: "/images/product-10.jpg",
shortDesc: "تامین‌کننده ویتامین K3 برای انعقاد خون و سلامت عمومی",
description: [
"ویتامین K3 نقش کلیدی در فرآیند انعقاد خون و سلامت عمومی دام و طیور دارد و کمبود آن می‌تواند منجر به خونریزی‌های داخلی و کاهش راندمان تولید شود.",
"این پرمیکس با غلظت استاندارد، برای استفاده در فرمولاسیون خوراک کارخانه‌ای و مزرعه‌ای مناسب است.",
],
packaging: "بسته‌بندی ۲۵ کیلوگرمی",
usage: "توسط کارخانجات خوراک دام و طیور، طبق فرمول استاندارد در پرمیکس نهایی گنجانده می‌شود.",
benefits: [
"پشتیبانی از فرآیند طبیعی انعقاد خون",
"غلظت استاندارد و قابل اطمینان",
"سازگار با سایر اجزای پرمیکس",
"کنترل کیفی در هر batch تولید",
],
},
{
id: 11,
slug: "premix-vitamin-d3",
title: "پرمیکس ویتامین D3",
category: "پرمیکس",
image: "/images/product-11.jpg",
shortDesc: "ضروری برای جذب کلسیم و استخوان‌بندی سالم",
description: [
"ویتامین D3 نقش تعیین‌کننده‌ای در جذب کلسیم و فسفر و در نتیجه استحکام استخوان و کیفیت پوسته تخم‌مرغ دارد.",
"این پرمیکس با پایداری بالا، برای نگهداری طولانی‌مدت و مصرف در شرایط مختلف اقلیمی مناسب است.",
],
packaging: "بسته‌بندی ۲۵ کیلوگرمی",
usage: "طبق فرمول استاندارد پرمیکس، توسط واحدهای تولید خوراک دام و طیور استفاده می‌شود.",
benefits: [
"بهبود جذب کلسیم و فسفر",
"افزایش استحکام استخوان و پوسته تخم‌مرغ",
"پایداری بالا در نگهداری",
"مناسب برای فرمولاسیون‌های صنعتی",
],
},
{
id: 12,
slug: "premix-vitamin-a",
title: "پرمیکس ویتامین A",
category: "پرمیکس",
image: "/images/product-12.jpg",
shortDesc: "پشتیبان سیستم ایمنی، بینایی و باروری",
description: [
"ویتامین A در حفظ سلامت پوشش‌های مخاطی، سیستم ایمنی، بینایی و عملکرد تولیدمثلی دام و طیور نقش اساسی دارد.",
"این پرمیکس با غلظت دقیق و کنترل‌شده، در فرمولاسیون خوراک صنعتی مورد استفاده قرار می‌گیرد.",
],
packaging: "بسته‌بندی ۲۵ کیلوگرمی",
usage: "طبق فرمول استاندارد پرمیکس و توسط کارشناسان تغذیه در ترکیب خوراک گنجانده می‌شود.",
benefits: [
"تقویت سیستم ایمنی بدن",
"پشتیبانی از سلامت پوشش‌های مخاطی",
"بهبود عملکرد تولیدمثلی",
"غلظت دقیق و قابل اطمینان",
],
},
{
id: 13,
slug: "poultry-concentrate",
title: "کنسانتره طیور",
category: "طیور",
image: "/images/category-4.jpg",
shortDesc: "خوراک کامل و متعادل برای دوره رشد و تولید طیور",
description: [
"کنسانتره طیور آراد آریسمان با ترکیبی کامل از پروتئین، انرژی، ویتامین و مواد معدنی، نیاز تغذیه‌ای طیور در مراحل مختلف پرورش را پوشش می‌دهد.",
"این محصول برای پرورش‌دهندگان صنعتی و نیمه‌صنعتی طراحی شده و کیفیت یکنواختی در هر بچ تولید دارد.",
],
packaging: "کیسه ۲۵ کیلوگرمی",
usage: "با نسبت توصیه‌شده در فرمول جیره، متناسب با سن و نوع طیور مصرف شود.",
benefits: [
"پوشش کامل نیاز تغذیه‌ای طیور",
"افزایش راندمان تولید",
"کیفیت یکنواخت در هر بچ",
"کاهش هزینه نهایی تولید",
],
},
{
id: 14,
slug: "sheep-supplement",
title: "مکمل گوسفند",
category: "گوسفند",
image: "/images/category-7.jpg",
shortDesc: "مکمل معدنی و ویتامینه برای سلامت و باروری گله گوسفند",
description: [
"این مکمل با تامین ویتامین‌ها و عناصر معدنی کمیاب، از سلامت عمومی، باروری و مقاومت گله گوسفند در برابر تنش‌های محیطی پشتیبانی می‌کند.",
"مصرف منظم آن به‌ویژه در دوره‌های حساس تولیدمثل و شیردهی توصیه می‌شود.",
],
packaging: "کیسه ۲۵ کیلوگرمی",
usage: "طبق دستور کارشناس دامپروری و متناسب با دوره فیزیولوژیک گله مصرف شود.",
benefits: [
"بهبود باروری و درصد زایش",
"تقویت سیستم ایمنی گله",
"پشتیبانی از دوره شیردهی",
"کاهش تلفات بره در دوره پرورش",
],
},
{
id: 15,
slug: "honeybee-supplement",
title: "مکمل زنبور عسل",
category: "زنبور عسل",
image: "/images/category-8.jpg",
shortDesc: "تغذیه کمکی برای تقویت کلنی در فصول کم‌گل",
description: [
"مکمل زنبور عسل آراد آریسمان به عنوان منبع تغذیه کمکی، در فصول کمبود گل و شهد، از قدرت و بقای کلنی پشتیبانی می‌کند.",
"این محصول با ترکیبی متعادل از قند، پروتئین و ریزمغذی‌ها، به حفظ فعالیت و تولیدمثل کلنی کمک می‌کند.",
],
packaging: "بسته‌بندی ۱ و ۵ کیلوگرمی",
usage: "طبق راهنمای زنبورداری، معمولا در فصول پاییز و اوایل بهار مورد استفاده قرار می‌گیرد.",
benefits: [
"تقویت کلنی در فصول کم‌گل",
"پشتیبانی از تولیدمثل ملکه",
"افزایش مقاومت کلنی در زمستان",
"ترکیب متعادل قند و پروتئین",
],
},
];
export function getProductBySlug(slug: string) {
return products.find((p) => p.slug === slug);
}
export function getRelatedProducts(product: Product, limit = 3) {
return products
.filter((p) => p.id !== product.id && p.category === product.category)
.slice(0, limit);
}
+210
View File
@@ -0,0 +1,210 @@
import { toFa } from "@/lib/format";
const API_BASE = "https://api.meshkee.com/api/v1";
export const TENANT_DOMAIN = "aradarisman.com";
export type ProductStore = {
variantCount: number;
minPrice: number | null;
maxPrice: number | null;
inStock: boolean;
};
export type Product = {
id: string;
businessId: string;
title: string;
nameFa: string;
summary: string;
descriptionHtml: string;
slug: string;
status: string;
categoryId: string | null;
categoryName: string | null;
categoryNameFa: string | null;
brandId: string | null;
brand: string | null;
tags: string[];
thumbnailUrl: string | null;
thumbnail: string | null;
image: string | null;
images: string[];
commentCount: number;
variantCount: number;
createdAt: string;
updatedAt: string;
store: ProductStore;
};
export type ProductListResponse = {
items: Product[];
total: number;
page: number;
pageSize: number;
};
async function meshkeeFetch<T>(path: string, revalidateSeconds = 300): Promise<T> {
const res = await fetch(`${API_BASE}${path}`, {
next: { revalidate: revalidateSeconds },
});
if (!res.ok) {
throw new Error(`Meshkee API ${path} failed: ${res.status}`);
}
return res.json();
}
export function getProducts(params: {
page?: number;
pageSize?: number;
categoryId?: string;
name?: string;
} = {}): Promise<ProductListResponse> {
const query = new URLSearchParams();
if (params.page) query.set("page", String(params.page));
if (params.pageSize) query.set("pageSize", String(params.pageSize));
if (params.categoryId) query.set("categoryId", params.categoryId);
if (params.name) query.set("name", params.name);
const qs = query.toString();
return meshkeeFetch<ProductListResponse>(
`/tenants/${TENANT_DOMAIN}/products${qs ? `?${qs}` : ""}`
);
}
export async function getProduct(slug: string): Promise<Product | null> {
const res = await fetch(
`${API_BASE}/tenants/${TENANT_DOMAIN}/products/${slug}`,
{ next: { revalidate: 300 } }
);
if (res.status === 404) return null;
if (!res.ok) throw new Error(`Meshkee API product "${slug}" failed: ${res.status}`);
const data = await res.json();
return data.product as Product;
}
export function productImage(product: Product): string | null {
return product.thumbnail || product.image || product.thumbnailUrl || null;
}
export function productPriceLabel(product: Product): string {
if (product.store?.minPrice) {
const formatted = new Intl.NumberFormat("fa-IR").format(product.store.minPrice);
return `${formatted} تومان`;
}
return "تماس بگیرید";
}
export function stripHtml(html: string, maxLength = 140): string {
const text = html
.replace(/<[^>]*>/g, " ")
.replace(/&nbsp;/g, " ")
.replace(/\s+/g, " ")
.trim();
if (text.length <= maxLength) return text;
return text.slice(0, maxLength).trim() + "…";
}
/**
* Blog field names are inferred from the Product/Category convention on this
* same backend (bilingual title/nameFa, descriptionHtml, thumbnail/image,
* categoryNameFa) since no tenant currently has published posts to verify
* against. Accessors below fall back across the plausible field names so the
* UI keeps working once real posts appear, even if the exact name differs.
*/
export type Post = {
id: string;
businessId?: string;
title?: string;
titleFa?: string;
nameFa?: string;
summary?: string;
excerpt?: string;
descriptionHtml?: string;
contentHtml?: string;
content?: string;
slug: string;
status?: string;
categoryId?: string | null;
categoryName?: string | null;
categoryNameFa?: string | null;
tags?: string[];
thumbnail?: string | null;
thumbnailUrl?: string | null;
image?: string | null;
images?: string[];
commentCount?: number;
publishedAt?: string | null;
createdAt: string;
updatedAt?: string;
};
export type PostListResponse = {
items: Post[];
total: number;
page: number;
pageSize: number;
};
export function getPosts(params: {
page?: number;
pageSize?: number;
categoryId?: string;
title?: string;
type?: "news" | "article" | "blog";
} = {}): Promise<PostListResponse> {
const query = new URLSearchParams();
if (params.page) query.set("page", String(params.page));
if (params.pageSize) query.set("pageSize", String(params.pageSize));
if (params.categoryId) query.set("categoryId", params.categoryId);
if (params.title) query.set("title", params.title);
if (params.type) query.set("type", params.type);
const qs = query.toString();
return meshkeeFetch<PostListResponse>(
`/tenants/${TENANT_DOMAIN}/blogs${qs ? `?${qs}` : ""}`
);
}
export async function getPost(slug: string): Promise<Post | null> {
const res = await fetch(`${API_BASE}/tenants/${TENANT_DOMAIN}/blogs/${slug}`, {
next: { revalidate: 300 },
});
if (res.status === 404) return null;
if (!res.ok) throw new Error(`Meshkee API blog "${slug}" failed: ${res.status}`);
const data = await res.json();
return (data.blog ?? data.post ?? data) as Post;
}
export function postTitle(post: Post): string {
return post.nameFa || post.titleFa || post.title || "بدون عنوان";
}
export function postContent(post: Post): string {
return post.descriptionHtml || post.contentHtml || post.content || "";
}
export function postImage(post: Post): string | null {
return post.thumbnail || post.image || post.thumbnailUrl || null;
}
export function postExcerpt(post: Post, maxLength = 140): string {
if (post.summary) return post.summary;
if (post.excerpt) return post.excerpt;
return stripHtml(postContent(post), maxLength);
}
export function postDate(post: Post): string {
const iso = post.publishedAt || post.createdAt;
return new Intl.DateTimeFormat("fa-IR-u-ca-persian", {
year: "numeric",
month: "long",
day: "numeric",
}).format(new Date(iso));
}
export function postReadingTime(post: Post): string {
const text = stripHtml(postContent(post), 100000);
const words = text.split(/\s+/).filter(Boolean).length;
const minutes = Math.max(1, Math.round(words / 150));
return `${toFa(minutes)} دقیقه مطالعه`;
}