fix seo and first page and 404 page

This commit is contained in:
2026-02-19 22:57:49 +03:30
parent 7b73faf32d
commit 234315ce52
13 changed files with 176 additions and 29 deletions
+6
View File
@@ -1,3 +1,9 @@
export const metadata = {
title: "درباره ما",
description:
"فیپ یک گروه متخصص در حوزه اینتگریشن و روش‌های نوین یکپارچه‌سازی. ماموریت، چشم‌انداز و ارزش‌های ما.",
};
export default function AboutPage() {
return (
<>
+47
View File
@@ -0,0 +1,47 @@
import { revalidateTag } from "next/cache";
import { NextRequest, NextResponse } from "next/server";
/**
* پاک کردن کش مقالات وردپرس.
* وقتی در وردپرس مقاله‌ای منتشر یا ویرایش شد، این آدرس را با secret فراخوانی کنید
* تا کش دانشنامه و مقاله‌ها بدون ریستارت سرور به‌روز شود.
*
* استفاده در وردپرس:
* - با پلاگین Webhook یا کد تم: بعد از save_post درخواست به این آدرس بفرست.
* - مثال: GET یا POST /api/revalidate?secret=YOUR_REVALIDATE_SECRET
* - یا هدر: x-revalidate-secret: YOUR_REVALIDATE_SECRET
*/
export async function GET(request: NextRequest) {
return handleRevalidate(request);
}
export async function POST(request: NextRequest) {
return handleRevalidate(request);
}
async function handleRevalidate(request: NextRequest) {
const secret = process.env.REVALIDATE_SECRET;
if (!secret) {
return NextResponse.json(
{ error: "REVALIDATE_SECRET تنظیم نشده است." },
{ status: 500 }
);
}
const provided =
request.nextUrl.searchParams.get("secret") ??
request.headers.get("x-revalidate-secret");
if (provided !== secret) {
return NextResponse.json({ error: "غیرمجاز" }, { status: 401 });
}
try {
revalidateTag("blog-posts");
return NextResponse.json({ revalidated: true, now: Date.now() });
} catch (err) {
return NextResponse.json(
{ error: "خطا در پاک کردن کش" },
{ status: 500 }
);
}
}
+19 -3
View File
@@ -20,17 +20,33 @@ interface BlogPostPageProps {
params: Promise<{ slug: string }>;
}
function stripHtml(html: string): string {
return html.replace(/<[^>]*>/g, "").trim();
}
export async function generateMetadata({
params,
}: BlogPostPageProps): Promise<Metadata> {
const { slug } = await params;
const post = await getPostBySlug(slug);
if (!post) return { title: "پست یافت نشد" };
const title = post.title?.rendered ?? "پست";
const excerpt = post.excerpt?.rendered?.replace(/<[^>]*>/g, "").trim().slice(0, 160) ?? "";
const titleRaw = post.title?.rendered ?? "پست";
const title = stripHtml(titleRaw);
const excerpt = stripHtml(post.excerpt?.rendered ?? "").slice(0, 160);
const imageUrl = getFeaturedImageUrl(post, "medium_large");
return {
title,
title: titleRaw,
description: excerpt,
openGraph: {
title,
description: excerpt,
...(imageUrl && { images: [{ url: imageUrl, alt: title }] }),
},
twitter: {
card: "summary_large_image",
title,
description: excerpt,
},
};
}
+4
View File
@@ -11,6 +11,10 @@ const PER_PAGE = 9;
export const metadata: Metadata = {
title: "دانشنامه",
description: "مقالات و مطالب فیپ در زمینه یکپارچه‌سازی، اتوماسیون و بهره‌وری سازمانی.",
openGraph: {
title: "دانشنامه",
description: "مقالات و مطالب فیپ در زمینه یکپارچه‌سازی، اتوماسیون و بهره‌وری سازمانی.",
},
};
interface BlogPageProps {
+1 -1
View File
@@ -115,7 +115,7 @@ const faqItems = [
]
export const metadata = {
title: 'مشاوره سازمانی | فیپ',
title: 'مشاوره سازمانی',
description:
'مشاوره تخصصی در حوزه اینتگریشن، هوشمندسازی، RAG، Performance و معماری نرم‌افزار. در سریع‌ترین زمان با شما تماس می‌گیریم.',
}
+27 -9
View File
@@ -1,4 +1,5 @@
import './globals.css'
import Link from 'next/link'
export const metadata = {
title: '۴۰۴ | صفحه یافت نشد',
@@ -8,15 +9,32 @@ export const metadata = {
export default function GlobalNotFound() {
return (
<html lang="fa" dir="rtl" suppressHydrationWarning>
<body className="antialiased min-h-svh flex flex-col items-center justify-center gap-6 px-4 bg-background text-foreground">
<h1 className="text-4xl font-bold">۴۰۴</h1>
<p className="text-muted-foreground">این صفحه وجود ندارد.</p>
<a
href="/"
className="rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground hover:opacity-90"
>
بازگشت به صفحه اصلی
</a>
<body className="antialiased min-h-svh flex flex-col items-center justify-center px-4 py-20 bg-background text-foreground">
<div className="mx-auto max-w-lg w-full text-center">
<div className="relative inline-block mb-6">
<span
className="text-[8rem] md:text-[10rem] font-black leading-none tracking-tighter text-transparent bg-clip-text bg-gradient-to-b from-primary/90 to-primary/50 select-none"
aria-hidden
>
۴۰۴
</span>
</div>
<h1 className="text-2xl font-bold text-foreground mb-3">
صفحه پیدا نشد
</h1>
<p className="text-muted-foreground mb-10 max-w-sm mx-auto leading-relaxed">
به نظر می‌رسد این آدرس وجود ندارد یا منتقل شده است.
</p>
<Link
href="/"
className="inline-flex items-center justify-center gap-2 h-11 px-6 rounded-md bg-primary text-primary-foreground font-medium hover:opacity-90 transition-opacity"
>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
صفحه اصلی
</Link>
</div>
</body>
</html>
)
+9 -3
View File
@@ -5,10 +5,16 @@ import { ThemeProvider } from "@/components/theme-provider";
import { TopProgressBar } from "@/components/top-progress-bar";
const SITE_NAME = "فیپ";
const DEFAULT_DESCRIPTION =
"فیپ یک سامانه برای ایجاد و طراحی راهکارهای مناسب برای کسب و کار شما است.";
export const metadata: Metadata = {
title: "فیپ",
description:
"فیپ یک سامانه برای ایجاد و طراحی راهکارهای مناسب برای کسب و کار شما است.",
title: {
default: `${SITE_NAME} | راهکارهای یکپارچه و هوشمند برای کسب‌وکار`,
template: `%s | ${SITE_NAME}`,
},
description: DEFAULT_DESCRIPTION,
icons: {
icon: "/assets/feep-Logo.svg",
},
+6
View File
@@ -1,5 +1,11 @@
import type { Metadata } from "next";
import { LoginForm } from "@/components/login-form";
export const metadata: Metadata = {
title: "ورود",
description: "ورود به حساب فیپ",
};
export default function LoginPage() {
return (
<div className="container mx-auto flex min-h-screen items-center justify-center px-4 py-16">
+30 -12
View File
@@ -1,21 +1,39 @@
import Link from 'next/link'
import { Button } from '@/components/ui/button'
import { Home } from 'lucide-react'
export const metadata = {
title: '۴۰۴',
description: 'صفحه‌ای که دنبال آن هستید یافت نشد.',
}
export default function NotFound() {
return (
<div className="flex min-h-[60vh] flex-col items-center justify-center gap-6 px-4 text-center">
<h1 className="text-4xl font-bold text-foreground">۴۰۴</h1>
<p className="text-muted-foreground max-w-md">
صفحه‌ای که دنبال آن هستید یافت نشد.
</p>
<div className="flex flex-wrap gap-3 justify-center">
<Button asChild>
<Link href="/">صفحه اصلی</Link>
</Button>
<Button asChild variant="outline">
<Link href="/consulting">مشاوره سازمانی</Link>
<section className="min-h-[70vh] flex items-center justify-center px-4 py-20">
<div className="mx-auto max-w-lg w-full text-center">
<div className="relative inline-block mb-6">
<span
className="text-[8rem] md:text-[10rem] font-black leading-none tracking-tighter text-transparent bg-clip-text bg-gradient-to-b from-primary/90 to-primary/50 select-none"
aria-hidden
>
۴۰۴
</span>
</div>
<h1 className="text-2xl font-bold text-foreground mb-3">
صفحه پیدا نشد
</h1>
<p className="text-muted-foreground mb-10 max-w-sm mx-auto leading-relaxed">
به نظر می‌رسد این آدرس وجود ندارد یا منتقل شده است.
</p>
<Button asChild size="lg" className="gap-2">
<Link href="/">
<Home className="size-5" />
صفحه اصلی
</Link>
</Button>
</div>
</div>
</section>
)
}
+6
View File
@@ -1,9 +1,15 @@
import type { Metadata } from "next";
import ContentSection from "@/components/content-1";
import Features from "@/components/features-12";
import { FeaturedPostsGrid } from "@/components/featured-posts-grid";
import { getPosts } from "@/lib/wp-api";
import HeroSection from "@/components/hero-section";
export const metadata: Metadata = {
description:
"فیپ؛ راهکارهای یکپارچه و هوشمند برای اتصال سیستم‌ها، اتوماسیون فرآیندها و بهره‌وری کسب‌وکار.",
};
const FEATURED_CATEGORY_ID = 3;
const FEATURED_POSTS_COUNT = 4;
+6
View File
@@ -1,6 +1,12 @@
import type { Metadata } from "next";
import Pricing from "@/components/pricing";
import PricingComparator from "@/components/pricing-comparator";
export const metadata: Metadata = {
title: "قیمت‌گذاری",
description: "تعرفه و پلن‌های خدمات فیپ. انتخاب مناسب برای کسب‌وکار شما.",
};
export default function PricingPage() {
return (
<>
+7
View File
@@ -1,7 +1,14 @@
import type { Metadata } from "next";
import { Button } from "@/components/ui/button";
import { Link as LinkIcon, BarChart3, Share2, Zap, TrendingUp, Workflow } from "lucide-react";
import Link from "next/link";
export const metadata: Metadata = {
title: "راه حل‌ها",
description:
"راهکارهای فیپ: یکپارچه‌سازی سیستم‌ها، پنل‌های مدیریتی، اتوماسیون فرآیندها و تحلیل داده.",
};
const solutions = [
{
icon: LinkIcon,
+8 -1
View File
@@ -1,6 +1,10 @@
const WP_API_BASE = "https://wpa.feep.ir/wp-json/wp/v2";
const FETCH_TIMEOUT_MS = 15_000;
/** کش مقالات وردپرس: یک ساعت. با revalidateTag('blog-posts') از API می‌توان کش را پاک کرد. */
const CACHE_REVALIDATE_SECONDS = 3600;
const CACHE_TAG_BLOG = "blog-posts";
export interface WpMediaSize {
source_url: string;
width?: number;
@@ -86,7 +90,10 @@ async function fetchWithTimeout(
const res = await fetch(url, {
...options,
signal: controller.signal,
next: { revalidate: 0 },
next: {
revalidate: CACHE_REVALIDATE_SECONDS,
tags: [CACHE_TAG_BLOG],
},
});
clearTimeout(timeout);
return res;