import { createContext, useCallback, useContext, useEffect, useState } from 'react' import type { ReactNode } from 'react' import type { Article, Project, Service } from './data/site' import { API_URL } from './config' // انواع داده‌هایی که بک‌اند در GET /api/site برمی‌گرداند export type BrandInfo = { id: number name: string logo: string hero: string contact_bg: string phone: string phone_href: string email: string address: string } export type TestimonialItem = { id: number; name: string; role: string; quote: string } export type FaqItem = { id: number; question: string; answer: string } export type TeamMemberItem = { id: number; name: string; role: string; bio: string } export type TimelineItem = { id: number; year: string; title: string; desc: string; image: string } export type StatItem = { id: number; value: number; suffix: string; label: string } export type WhyUsItem = { id: number; title: string; desc: string } export type BeforeAfterInfo = { id: number title: string lead: string before: string after: string before_label: string after_label: string } export type NavItem = { id: number; label: string; href: string } export type HeroContent = { id: number kicker: string title: string title_highlight: string subtitle: string badge_value: string badge_label: string cta_primary_label: string cta_primary_href: string cta_secondary_label: string cta_secondary_href: string scroll_label: string } export type StoryContent = { id: number title: string paragraphs: string[] quote: string quote_author: string stat1_value: string stat1_label: string stat2_value: string stat2_label: string link_label: string link_href: string bento: { src: string; alt: string }[] } export type SectionHeadingContent = { id: number key: string label: string title: string lead: string image: string cta_label: string cta_href: string } export type PageHeroContent = { id: number; key: string; title: string; lead: string; image: string } export type InvestmentModelItem = { id: number; title: string; desc: string; points: string[] } export type ProcessStepItem = { id: number; step: string; title: string; desc: string } export type OfficeItem = { id: number; city: string; address: string; hours: string } export type GalleryImageItem = { id: number; src: string; alt: string } export type StatusLabelItem = { id: number; key: string; label: string } export type ContactSectionContent = { id: number label: string title: string lead: string address_label: string phone_label: string email_label: string name_label: string name_placeholder: string phone_field_label: string phone_placeholder: string note_label: string note_optional_label: string note_placeholder: string submit_label: string sending_label: string success_title: string success_text: string success_button_label: string } export type NewsletterContent = { id: number title: string lead: string placeholder: string button_label: string sending_label: string success_text: string } export type SocialItem = { label: string; href: string; icon: string } export type FooterContent = { id: number about_text: string pages_heading: string services_heading: string contact_heading: string copyright: string tagline: string socials: SocialItem[] } export interface SiteData { brand: BrandInfo nav: NavItem[] hero: HeroContent story: StoryContent section_headings: SectionHeadingContent[] page_heroes: PageHeroContent[] services: Service[] projects: Project[] articles: Article[] testimonials: TestimonialItem[] faqs: FaqItem[] team: TeamMemberItem[] timeline: TimelineItem[] stats: StatItem[] why_us: WhyUsItem[] before_after: BeforeAfterInfo investment_models: InvestmentModelItem[] process_steps: ProcessStepItem[] offices: OfficeItem[] gallery: GalleryImageItem[] status_labels: StatusLabelItem[] contact_section: ContactSectionContent newsletter: NewsletterContent footer: FooterContent seo_meta: { id: number; page_key: string; title: string; description: string }[] } interface SiteContextValue { data: SiteData | null error: boolean reload: () => void /** به‌روزرسانی خوش‌بینانه درخت داده — پس از موفقیت PATCH استفاده می‌شود */ updateSite: (mutate: (data: SiteData) => void) => void } const SiteContext = createContext({ data: null, error: false, reload: () => {}, updateSite: () => {}, }) export function SiteProvider({ children }: { children: ReactNode }) { const [data, setData] = useState(null) const [error, setError] = useState(false) const load = useCallback(() => { let cancelled = false setError(false) fetch(`${API_URL}/api/site`) .then((res) => { if (!res.ok) throw new Error(`HTTP ${res.status}`) return res.json() as Promise }) .then((json) => { if (!cancelled) { if (json.brand && json.hero && json.footer && json.nav) setData(json) else setError(true) } }) .catch(() => { if (!cancelled) setError(true) }) return () => { cancelled = true } }, []) useEffect(() => load(), [load]) const reload = useCallback(() => { setData(null) setError(false) load() }, [load]) const updateSite = useCallback((mutate: (data: SiteData) => void) => { setData((prev) => { if (!prev) return prev const next = structuredClone(prev) mutate(next) return next }) }, []) return ( {children} ) } export function useSiteData(): SiteContextValue { return useContext(SiteContext) } // فقط در کامپوننت‌هایی استفاده شود که پس از بارگذاری کامل داده‌ها رندر می‌شوند export function useSite(): SiteData { const { data } = useContext(SiteContext) if (!data) { throw new Error('useSite باید پس از بارگذاری داده‌های سایت استفاده شود') } return data } const EMPTY_HEADING: SectionHeadingContent = { id: 0, key: '', label: '', title: '', lead: '', image: '', cta_label: '', cta_href: '', } /** تیتر و توضیح یک بخش بر اساس key تعریف‌شده در پنل مدیریت */ export function useSectionHeading(key: string): SectionHeadingContent { const { data } = useContext(SiteContext) return data?.section_headings.find((s) => s.key === key) ?? { ...EMPTY_HEADING, key } } /** هیرو بالای صفحه بر اساس key تعریف‌شده در پنل مدیریت */ export function usePageHero(key: string): PageHeroContent { const { data } = useContext(SiteContext) return ( data?.page_heroes.find((p) => p.key === key) ?? { id: 0, key, title: '', lead: '', image: '' } ) } /** برچسب فارسی وضعیت پروژه (done | progress) از پنل مدیریت */ export function useStatusLabel(key: string): string { const { data } = useContext(SiteContext) return data?.status_labels.find((s) => s.key === key)?.label ?? key }