diff --git a/frontend/src/api.tsx b/frontend/src/api.tsx index a98f447..36263b7 100644 --- a/frontend/src/api.tsx +++ b/frontend/src/api.tsx @@ -249,6 +249,24 @@ export function SiteProvider({ children }: { children: ReactNode }) { setError(false) const controller = new AbortController() const timeoutId = window.setTimeout(() => controller.abort(), SITE_TIMEOUT_MS) + // درخواست‌های سایت و پیش‌نویس‌های ادمین «همزمان» شروع می‌شوند — + // پشت سر هم بودنشان یعنی ادمین دو برابر دیرتر محتوا را می‌بیند. + const draftsPromise: Promise = getToken() + ? fetch(`${API_URL}/api/admin/articles`, { + headers: { Authorization: `Bearer ${getToken()}` }, + signal: controller.signal, + }) + .then(async (res) => { + if (res.status === 401) { + // توکن منقضی/نامعتبر — پاک می‌شود تا بارگذاری‌های بعدی درخواست بی‌فایده نفرستند + setToken(null) + return null + } + return res.ok ? ((await res.json()) as Article[]) : null + }) + .catch(() => null) + : Promise.resolve(null) + fetch(`${API_URL}/api/site`, { signal: controller.signal }) .then((res) => { if (!res.ok) throw new Error(`HTTP ${res.status}`) @@ -256,25 +274,12 @@ export function SiteProvider({ children }: { children: ReactNode }) { }) .then(async (json) => { if (cancelled) return - // برای ادمین واردشده، پیش‌نویس مقاله‌ها از انتهای مدیریتی خوانده و ادغام می‌شوند — - // بک‌اند در GET /api/site فقط مقاله‌های منتشرشده را برمی‌گرداند - if (getToken()) { - try { - const res = await fetch(`${API_URL}/api/admin/articles`, { - headers: { Authorization: `Bearer ${getToken()}` }, - }) - if (res.status === 401) { - // توکن منقضی/نامعتبر — پاک می‌شود تا بارگذاری‌های بعدی درخواست بی‌فایده نفرستند - setToken(null) - } else if (res.ok) { - const all = (await res.json()) as Article[] - const published = new Set(json.articles.map((a) => a.id)) - const drafts = all.filter((a) => a.status === 'draft' && !published.has(a.id)) - json.articles = [...json.articles, ...drafts] - } - } catch { - /* بدون پیش‌نویس‌ها ادامه بده — بارگذاری سایت هرگز وابسته به پنل مدیریتی نیست */ - } + // بک‌اند در GET /api/site فقط مقاله‌های منتشرشده را برمی‌گرداند — + // پیش‌نویس‌ها (برای ادمین) از درخواست موازی ادغام می‌شوند + const drafts = await draftsPromise + if (drafts) { + const published = new Set(json.articles.map((a) => a.id)) + json.articles = [...json.articles, ...drafts.filter((a) => a.status === 'draft' && !published.has(a.id))] } if (!cancelled) { if (json.brand && json.hero && json.footer && json.nav) setData(json) diff --git a/frontend/src/components/home/Hero.tsx b/frontend/src/components/home/Hero.tsx index c3a0dd2..62c66d4 100644 --- a/frontend/src/components/home/Hero.tsx +++ b/frontend/src/components/home/Hero.tsx @@ -51,7 +51,7 @@ export function Hero() { saveHero('kicker', v)} /> @@ -61,7 +61,7 @@ export function Hero() { saveHero('title', v)} aria-label={t('heroTitleAria')} /> @@ -71,7 +71,7 @@ export function Hero() { saveHero('subtitle', v)} /> @@ -81,7 +81,7 @@ export function Hero() { {/* دکمه اصلی: کپسولی شیشه‌ای دودی با دایره کهربایی اختصاصی برای آیکون */} @@ -113,7 +113,7 @@ export function Hero() {