mirror of
https://git.meshkee.com/Meshkee-Websites/arad-arisman.git
synced 2026-08-11 20:29:33 +04:30
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:
@@ -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",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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
@@ -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>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -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(/ /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)} دقیقه مطالعه`;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user