From 0fa87e2d9a75d5d21ee996f53f739349769311e5 Mon Sep 17 00:00:00 2001 From: nineap Date: Thu, 9 Jul 2026 16:37:06 +0700 Subject: [PATCH] =?UTF-8?q?=D0=A4=D0=BE=D0=BB=D0=B1=D1=8D=D0=BA=20=D0=BD?= =?UTF-8?q?=D0=B0=20=D0=BA=D1=8D=D1=88=20=D0=BF=D1=80=D0=B8=20=D0=BD=D0=B5?= =?UTF-8?q?=D0=B4=D0=BE=D1=81=D1=82=D1=83=D0=BF=D0=BD=D0=BE=D1=81=D1=82?= =?UTF-8?q?=D0=B8=20PocketBase=20(Pricing/FAQ)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pricing: при полном отказе всех запросов пробует localStorage-кэш последнего успешного ответа вместо пустого состояния, пишет туда же при успехе. Faq: отличает "в кэше правда пусто" от "не смогли загрузить" — раньше оба случая показывали один и тот же текст "0 protocols found", что вводило в заблуждение при обычном сетевом сбое. Co-Authored-By: Claude Sonnet 5 --- components/sections/Faq.tsx | 13 ++++++++---- components/sections/Pricing.tsx | 37 ++++++++++++++++++++++++++++++++- dictionaries/ar.json | 6 ++++-- dictionaries/be.json | 6 ++++-- dictionaries/en.json | 6 ++++-- dictionaries/fa.json | 6 ++++-- dictionaries/ru.json | 6 ++++-- dictionaries/tr.json | 6 ++++-- dictionaries/uk.json | 6 ++++-- dictionaries/uz.json | 6 ++++-- dictionaries/vi.json | 6 ++++-- dictionaries/zh-CN.json | 6 ++++-- lib/IDict.ts | 2 ++ 13 files changed, 87 insertions(+), 25 deletions(-) diff --git a/components/sections/Faq.tsx b/components/sections/Faq.tsx index 245dc32..2927f46 100644 --- a/components/sections/Faq.tsx +++ b/components/sections/Faq.tsx @@ -25,10 +25,13 @@ export async function FAQ({ lang: string; }) { let faqs: PBFaq[] = []; + // Различаем "PocketBase ответил, но для этого языка коллекции ещё нет" (штатно, + // это и есть fallback на en ниже) от "PocketBase вообще не ответил ни разу" — + // раньше оба случая одинаково показывали "0 protocols found", что вводит в + // заблуждение при реальном сбое инфраструктуры (выглядит как "у нас нет FAQ", + // а не "сейчас не грузится"). + let hadFetchError = false; - // CMS-контент (в отличие от статичных dictionaries/*.json) переведён только - // для части языков — коллекции `faqs_` для новых языков ещё не заведены - // в PocketBase. Пока для них нет перевода, показываем английский, а не пустоту. for (const fallbackLang of Array.from(new Set([lang, "en"]))) { try { faqs = await pb.collection(`faqs_${fallbackLang}`).getFullList({ @@ -36,8 +39,10 @@ export async function FAQ({ fetch: (url, config) => fetch(url, { ...config, next: { revalidate: 86400 } }), // Кэшируем на сутки }); + hadFetchError = false; break; } catch (error) { + hadFetchError = true; console.error(`Ошибка загрузки FAQ (${fallbackLang}):`, error); } } @@ -88,7 +93,7 @@ export async function FAQ({ {faqs.length === 0 ? (

- 0 protocols found in cache. + {hadFetchError ? dict.loadError : "0 protocols found in cache."}

) : ( diff --git a/components/sections/Pricing.tsx b/components/sections/Pricing.tsx index 177612a..78084d3 100644 --- a/components/sections/Pricing.tsx +++ b/components/sections/Pricing.tsx @@ -63,9 +63,14 @@ export function Pricing({ const [period, setPeriod] = useState("1y"); const [plans, setPlans] = useState([]); const [loading, setLoading] = useState(!!lang); + // PocketBase недоступен целиком (не просто нет перевода для этого языка) — + // показываем последний успешно закэшированный список вместо пустой секции. + const [usingCache, setUsingCache] = useState(false); useEffect(() => { if (!lang) return; + const cacheKey = `netrunner_pricing_cache_${lang}`; + async function fetchPlans() { try { setLoading(true); @@ -74,6 +79,7 @@ export function Pricing({ // `pricing_` для новых языков ещё не заведена в PocketBase. // Пока для них нет перевода, показываем английские тарифы, а не пустоту. let records: PBPricingPlan[] = []; + let allFailed = true; for (const fallbackLang of Array.from(new Set([lang, "en"]))) { try { records = await pb @@ -82,11 +88,34 @@ export function Pricing({ sort: "order", requestKey: null, }); + allFailed = false; break; } catch (error) { console.error(`Ошибка загрузки тарифов (${fallbackLang}):`, error); } } + + if (allFailed) { + try { + const cached = localStorage.getItem(cacheKey); + if (cached) { + records = JSON.parse(cached); + setUsingCache(true); + } + } catch { + // localStorage недоступен (приватный режим и т.п.) — просто нет кеша. + } + } else { + setUsingCache(false); + if (records.length > 0) { + try { + localStorage.setItem(cacheKey, JSON.stringify(records)); + } catch { + // Диск/квота localStorage — не критично, просто не кешируем. + } + } + } + setPlans(records); } finally { setLoading(false); @@ -121,10 +150,16 @@ export function Pricing({ > {dict.badge} -

+

{dict.title}

+ {usingCache && ( +

+ {dict.offlineNotice} +

+ )} +
{periods.map((p: PeriodOption) => (