Moshavereh sazmani
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
const TELEGRAM_API = 'https://api.telegram.org'
|
||||
|
||||
export interface ConsultingRequestBody {
|
||||
fullName: string
|
||||
organization: string
|
||||
phone: string
|
||||
area: string
|
||||
description?: string
|
||||
}
|
||||
|
||||
function escapeForTelegram(text: string): string {
|
||||
return text
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
}
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const token = process.env.TELEGRAM_BOT_TOKEN
|
||||
const chatId = process.env.TELEGRAM_CHAT_ID
|
||||
|
||||
if (!token || !chatId) {
|
||||
return NextResponse.json(
|
||||
{ error: 'تلگرام پیکربندی نشده است. TELEGRAM_BOT_TOKEN و TELEGRAM_CHAT_ID را در .env.local تنظیم کنید.' },
|
||||
{ status: 500 }
|
||||
)
|
||||
}
|
||||
|
||||
let body: ConsultingRequestBody
|
||||
try {
|
||||
body = await request.json()
|
||||
} catch {
|
||||
return NextResponse.json({ error: 'بدنه درخواست نامعتبر است' }, { status: 400 })
|
||||
}
|
||||
|
||||
const { fullName, organization, phone, area, description } = body
|
||||
if (!fullName?.trim() || !organization?.trim() || !phone?.trim() || !area?.trim()) {
|
||||
return NextResponse.json({ error: 'فیلدهای الزامی خالی است' }, { status: 400 })
|
||||
}
|
||||
|
||||
const lines = [
|
||||
'📩 <b>درخواست جدید مشاوره سازمانی</b>',
|
||||
'',
|
||||
`<b>نام و نام خانوادگی:</b> ${escapeForTelegram(fullName.trim())}`,
|
||||
`<b>نام سازمان:</b> ${escapeForTelegram(organization.trim())}`,
|
||||
`<b>شماره تماس:</b> ${escapeForTelegram(phone.trim())}`,
|
||||
`<b>حوزه مشاوره:</b> ${escapeForTelegram(area.trim())}`,
|
||||
]
|
||||
if (description?.trim()) {
|
||||
lines.push('', `<b>توضیحات:</b>`, escapeForTelegram(description.trim()))
|
||||
}
|
||||
|
||||
const text = lines.join('\n')
|
||||
|
||||
try {
|
||||
const res = await fetch(`${TELEGRAM_API}/bot${token}/sendMessage`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
chat_id: chatId,
|
||||
text,
|
||||
parse_mode: 'HTML',
|
||||
}),
|
||||
})
|
||||
|
||||
const data = await res.json().catch(() => ({}))
|
||||
if (!res.ok) {
|
||||
return NextResponse.json(
|
||||
{ error: data.description || 'ارسال به تلگرام ناموفق بود' },
|
||||
{ status: 502 }
|
||||
)
|
||||
}
|
||||
return NextResponse.json({ ok: true })
|
||||
} catch (err) {
|
||||
return NextResponse.json(
|
||||
{ error: 'خطا در ارتباط با تلگرام' },
|
||||
{ status: 502 }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
import { AspectRatio } from '@/components/ui/aspect-ratio'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent } from '@/components/ui/card'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from '@/components/ui/accordion'
|
||||
import { ConsultingForm } from '@/components/consulting-form'
|
||||
import {
|
||||
Plug2,
|
||||
Sparkles,
|
||||
Database,
|
||||
Gauge,
|
||||
Shield,
|
||||
Layout,
|
||||
BarChart3,
|
||||
HelpCircle,
|
||||
} from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
|
||||
const consultants = [
|
||||
{
|
||||
name: 'مشاور ارشد اینتگریشن',
|
||||
expertise: ['اینتگریشن سیستمها', 'API', 'اتوماسیون'],
|
||||
description:
|
||||
'بیش از ده سال تجربه در طراحی و پیادهسازی راهکارهای یکپارچهسازی برای سازمانهای بزرگ.',
|
||||
image: '/assets/logo.svg',
|
||||
fallback: 'م۱',
|
||||
},
|
||||
{
|
||||
name: 'مشاور هوشمندسازی و RAG',
|
||||
expertise: ['RAG', 'هوش مصنوعی', 'پردازش زبان طبیعی'],
|
||||
description:
|
||||
'تخصص در طراحی سامانههای مبتنی بر دانش و هوشمندسازی فرآیندهای سازمانی.',
|
||||
image: '/assets/logo.svg',
|
||||
fallback: 'م۲',
|
||||
},
|
||||
{
|
||||
name: 'مشاور عملکرد و معماری',
|
||||
expertise: ['Performance', 'معماری نرمافزار', 'مقیاسپذیری'],
|
||||
description:
|
||||
'بهینهسازی عملکرد سامانهها و طراحی معماری مقیاسپذیر و پایدار.',
|
||||
image: '/assets/logo.svg',
|
||||
fallback: 'م۳',
|
||||
},
|
||||
]
|
||||
|
||||
const domains = [
|
||||
{
|
||||
title: 'اینتگریشن',
|
||||
description: 'اتصال سیستمها، APIها و یکپارچهسازی دادهها و فرآیندها',
|
||||
icon: Plug2,
|
||||
},
|
||||
{
|
||||
title: 'هوشمندسازی',
|
||||
description: 'اتوماسیون و هوشمندسازی فرآیندهای کسبوکار',
|
||||
icon: Sparkles,
|
||||
},
|
||||
{
|
||||
title: 'RAG',
|
||||
description: 'سامانههای بازیابی و تولید پاسخ مبتنی بر دانش',
|
||||
icon: Database,
|
||||
},
|
||||
{
|
||||
title: 'Performance',
|
||||
description: 'بهینهسازی عملکرد، مقیاسپذیری و پاسخگویی',
|
||||
icon: Gauge,
|
||||
},
|
||||
{
|
||||
title: 'امنیت',
|
||||
description: 'امنیت داده، احراز هویت و کنترل دسترسی',
|
||||
icon: Shield,
|
||||
},
|
||||
{
|
||||
title: 'معماری نرمافزار',
|
||||
description: 'طراحی معماری، میکروسرویس و طراحی سیستم',
|
||||
icon: Layout,
|
||||
},
|
||||
{
|
||||
title: 'داده و تحلیل',
|
||||
description: 'تحلیل داده، گزارشگیری و دشبوردهای تحلیلی',
|
||||
icon: BarChart3,
|
||||
},
|
||||
]
|
||||
|
||||
const faqItems = [
|
||||
{
|
||||
id: 'faq-1',
|
||||
question: 'مشاوره سازمانی چیست؟',
|
||||
answer:
|
||||
'مشاوره سازمانی خدمتی است که در آن متخصصان با بررسی وضعیت فعلی سازمان، چالشها و اهداف شما، راهکارهای فنی و فرآیندی پیشنهاد میدهند. این مشاوره میتواند در حوزههای اینتگریشن، هوشمندسازی، RAG، بهینهسازی عملکرد و معماری نرمافزار باشد.',
|
||||
},
|
||||
{
|
||||
id: 'faq-2',
|
||||
question: 'چه زمانی میتوانم انتظار تماس داشته باشم؟',
|
||||
answer:
|
||||
'پس از ثبت درخواست، تیم ما در سریعترین زمان ممکن (معمولاً حداکثر ۲۴ تا ۴۸ ساعت کاری) با شما تماس میگیرد تا نیازتان را دقیقتر بررسی کنیم و زمان جلسه مشاوره را هماهنگ کنیم.',
|
||||
},
|
||||
{
|
||||
id: 'faq-3',
|
||||
question: 'آیا مشاوره اولیه رایگان است؟',
|
||||
answer:
|
||||
'جلسه اول شناخت و تعیین scope معمولاً به صورت رایگان یا با هزینه نمادین انجام میشود. جزئیات بسته به نوع درخواست و حجم کار در اولین تماس به شما اعلام میشود.',
|
||||
},
|
||||
{
|
||||
id: 'faq-4',
|
||||
question: 'برای چه اندازهای از سازمانها مناسب است؟',
|
||||
answer:
|
||||
'خدمات مشاوره ما برای استارتاپها، شرکتهای کوچک و متوسط و سازمانهای بزرگ طراحی شده است. محتوای جلسات و پیشنهادها متناسب با اندازه و نیاز هر سازمان تنظیم میشود.',
|
||||
},
|
||||
]
|
||||
|
||||
export const metadata = {
|
||||
title: 'مشاوره سازمانی | فیپ',
|
||||
description:
|
||||
'مشاوره تخصصی در حوزه اینتگریشن، هوشمندسازی، RAG، Performance و معماری نرمافزار. در سریعترین زمان با شما تماس میگیریم.',
|
||||
}
|
||||
|
||||
export default function ConsultingPage() {
|
||||
return (
|
||||
<>
|
||||
{/* Section 1: Hero - مشاوره سازمانی */}
|
||||
<section className="bg-background pt-28 pb-16 md:pt-36 md:pb-24">
|
||||
<div className="mx-auto max-w-6xl px-4 md:px-6">
|
||||
<div className="grid gap-12 lg:grid-cols-2 lg:gap-16 items-center">
|
||||
<div className="space-y-8 order-2 lg:order-1">
|
||||
<div className="space-y-4">
|
||||
<h1 className="text-4xl font-bold tracking-tight md:text-5xl lg:text-6xl">
|
||||
مشاوره سازمانی
|
||||
</h1>
|
||||
<p className="text-lg text-muted-foreground leading-relaxed max-w-xl">
|
||||
مشاوره سازمانی به معنای همراهی تخصصی با سازمان شما برای شناسایی چالشها،
|
||||
انتخاب راهکارهای فنی مناسب و برنامهریزی برای پیادهسازی است. ما در حوزههای
|
||||
اینتگریشن، هوشمندسازی، RAG و بهینهسازی عملکرد در کنار شما هستیم.
|
||||
</p>
|
||||
<p className="text-muted-foreground leading-relaxed max-w-xl">
|
||||
شرایط همکاری: پس از ثبت درخواست، تیم ما با شما تماس میگیرد؛ جلسه اول برای
|
||||
شناخت نیازها و تعیین دامنه کار است و در ادامه پیشنهاد و برنامه اجرا ارائه
|
||||
میشود.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-3">
|
||||
<Button asChild size="lg">
|
||||
<Link href="#consulting-form">ثبت درخواست مشاوره</Link>
|
||||
</Button>
|
||||
<Button asChild variant="outline" size="lg">
|
||||
<Link href="#domains">حوزههای مشاوره</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative order-1 lg:order-2">
|
||||
<div className="grid gap-4">
|
||||
<AspectRatio ratio={4 / 3} className="overflow-hidden rounded-2xl bg-muted">
|
||||
<div className="flex h-full w-full items-center justify-center bg-gradient-to-br from-primary/10 to-primary/5">
|
||||
<HelpCircle className="size-24 text-primary/40" />
|
||||
</div>
|
||||
</AspectRatio>
|
||||
<AspectRatio ratio={16 / 9} className="overflow-hidden rounded-2xl bg-muted -mt-8 mr-8 border shadow-lg">
|
||||
<div className="flex h-full w-full items-center justify-center bg-muted/80" />
|
||||
</AspectRatio>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Section 2: مشاوران */}
|
||||
<section className="bg-muted/30 py-16 md:py-24" id="consultants">
|
||||
<div className="mx-auto max-w-6xl px-4 md:px-6">
|
||||
<div className="mx-auto max-w-2xl text-center mb-12">
|
||||
<h2 className="text-3xl font-bold md:text-4xl">چه کسانی مشاوره میدهند؟</h2>
|
||||
<p className="mt-4 text-muted-foreground">
|
||||
تیم مشاوره ما متشکل از متخصصان با تجربه در حوزههای مختلف فناوری است.
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid gap-8 md:grid-cols-3">
|
||||
{consultants.map((person) => (
|
||||
<Card key={person.fallback} className="overflow-hidden">
|
||||
<CardContent className="pt-6">
|
||||
<div className="flex flex-col items-center text-center space-y-4">
|
||||
<Avatar className="h-24 w-24">
|
||||
<AvatarImage src={person.image} alt={person.name} />
|
||||
<AvatarFallback className="text-xl">{person.fallback}</AvatarFallback>
|
||||
</Avatar>
|
||||
<div>
|
||||
<h3 className="font-bold text-lg">{person.name}</h3>
|
||||
<div className="flex flex-wrap justify-center gap-1.5 mt-2">
|
||||
{person.expertise.map((e) => (
|
||||
<Badge key={e} variant="secondary" className="text-xs">
|
||||
{e}
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground leading-relaxed">
|
||||
{person.description}
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Section 3: حوزههای مشاوره */}
|
||||
<section className="bg-background py-16 md:py-24" id="domains">
|
||||
<div className="mx-auto max-w-6xl px-4 md:px-6">
|
||||
<div className="mx-auto max-w-2xl text-center mb-12">
|
||||
<h2 className="text-3xl font-bold md:text-4xl">در چه حوزههایی مشاوره میدهیم؟</h2>
|
||||
<p className="mt-4 text-muted-foreground">
|
||||
از اینتگریشن و هوشمندسازی تا RAG و بهینهسازی عملکرد
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{domains.map((domain) => {
|
||||
const Icon = domain.icon
|
||||
return (
|
||||
<Card key={domain.title} className="overflow-hidden transition-all hover:shadow-md">
|
||||
<CardContent className="pt-6">
|
||||
<div className="flex gap-4">
|
||||
<div className="flex h-12 w-12 shrink-0 items-center justify-center rounded-lg bg-primary/10 text-primary">
|
||||
<Icon className="size-6" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-bold">{domain.title}</h3>
|
||||
<p className="text-sm text-muted-foreground mt-1 leading-relaxed">
|
||||
{domain.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Section 4: فرم درخواست مشاوره */}
|
||||
<section className="bg-muted/30 py-16 md:py-24" id="consulting-form">
|
||||
<div className="mx-auto max-w-6xl px-4 md:px-6">
|
||||
<div className="mx-auto max-w-xl">
|
||||
<div className="text-center mb-10">
|
||||
<h2 className="text-3xl font-bold md:text-4xl">درخواست مشاوره</h2>
|
||||
<p className="mt-4 text-muted-foreground">
|
||||
فرم زیر را پر کنید؛ در سریعترین زمان ممکن با شما تماس میگیریم.
|
||||
</p>
|
||||
</div>
|
||||
<Card className="p-6 md:p-8">
|
||||
<CardContent className="p-0">
|
||||
<ConsultingForm />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Section 5: سوالات متداول */}
|
||||
<section className="bg-background py-16 md:py-24">
|
||||
<div className="mx-auto max-w-3xl px-4 md:px-6">
|
||||
<div className="mx-auto max-w-xl text-center mb-12">
|
||||
<h2 className="text-3xl font-bold md:text-4xl">سوالات متداول</h2>
|
||||
<p className="mt-4 text-muted-foreground">
|
||||
پاسخ سوالات رایج درباره مشاوره سازمانی
|
||||
</p>
|
||||
</div>
|
||||
<Accordion type="single" collapsible className="w-full rounded-2xl border bg-muted/30 p-1">
|
||||
{faqItems.map((item) => (
|
||||
<AccordionItem
|
||||
key={item.id}
|
||||
value={item.id}
|
||||
className="rounded-xl border-none px-4 data-[state=open]:bg-card data-[state=open]:shadow-sm"
|
||||
>
|
||||
<AccordionTrigger className="text-start hover:no-underline py-5">
|
||||
{item.question}
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<p className="text-muted-foreground leading-relaxed pb-2">{item.answer}</p>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
))}
|
||||
</Accordion>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { Button } from '@/components/ui/button'
|
||||
|
||||
export default function Error({
|
||||
error,
|
||||
reset,
|
||||
}: {
|
||||
error: Error & { digest?: string }
|
||||
reset: () => void
|
||||
}) {
|
||||
useEffect(() => {
|
||||
console.error(error)
|
||||
}, [error])
|
||||
|
||||
return (
|
||||
<div className="flex min-h-[60vh] flex-col items-center justify-center gap-6 px-4 text-center">
|
||||
<h2 className="text-xl font-semibold text-foreground">خطایی رخ داد</h2>
|
||||
<p className="text-muted-foreground max-w-md text-sm">
|
||||
لطفاً دوباره تلاش کنید یا به صفحه اصلی برگردید.
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-3 justify-center">
|
||||
<Button onClick={() => reset()}>تلاش مجدد</Button>
|
||||
<Button asChild variant="outline">
|
||||
<Link href="/">صفحه اصلی</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import './globals.css'
|
||||
|
||||
export const metadata = {
|
||||
title: '۴۰۴ | صفحه یافت نشد',
|
||||
description: 'صفحهای که دنبال آن هستید وجود ندارد.',
|
||||
}
|
||||
|
||||
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>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import Link from 'next/link'
|
||||
import { Button } from '@/components/ui/button'
|
||||
|
||||
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>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
+2
-3
@@ -2,7 +2,6 @@ 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 { HeroHeader } from "@/components/header";
|
||||
import HeroSection from "@/components/hero-section";
|
||||
|
||||
const FEATURED_CATEGORY_ID = 3;
|
||||
@@ -17,9 +16,9 @@ export default async function Home() {
|
||||
orderby: "date",
|
||||
order: "desc",
|
||||
});
|
||||
featuredPosts = result.posts;
|
||||
featuredPosts = result?.posts ?? [];
|
||||
} catch {
|
||||
// بدون پست هم صفحه رندر میشود
|
||||
featuredPosts = [];
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user