diff --git a/src/app/[locale]/applications/[slug]/page.tsx b/src/app/[locale]/applications/[slug]/page.tsx index 4350e9a..3add71e 100644 --- a/src/app/[locale]/applications/[slug]/page.tsx +++ b/src/app/[locale]/applications/[slug]/page.tsx @@ -20,17 +20,28 @@ import JsonLd from "@/components/seo/JsonLd"; // --- FUNCIÓN ORIGINAL PARA LEER IMÁGENES LOCALES --- function getApplicationImages(slug: string) { - const imagesDir = path.join(process.cwd(), "public", "applications", slug); let blueprints: string[] = []; let machines: string[] = []; let heroImage = ""; - if (fs.existsSync(imagesDir)) { - const files = fs.readdirSync(imagesDir).filter(file => /\.(png|jpe?g|webp)$/i.test(file)); - - heroImage = files[0] ? `/applications/${slug}/${files[0]}` : ""; - blueprints = files.filter(f => f.includes("Screenshot") || f.startsWith("P10") || f.includes("blueprint")).slice(0, 3).map(f => `/applications/${slug}/${f}`); - machines = files.filter(f => !f.includes("Screenshot") && !f.startsWith("P10") && !f.includes("blueprint")).slice(1, 4).map(f => `/applications/${slug}/${f}`); + try { + const imagesDir = path.join(process.cwd(), "public", "applications", slug); + + if (fs.existsSync(imagesDir)) { + const files = fs.readdirSync(imagesDir).filter((file) => /\.(png|jpe?g|webp)$/i.test(file)); + + heroImage = files[0] ? `/applications/${slug}/${files[0]}` : ""; + blueprints = files + .filter((f) => f.includes("Screenshot") || f.startsWith("P10") || f.includes("blueprint")) + .slice(0, 3) + .map((f) => `/applications/${slug}/${f}`); + machines = files + .filter((f) => !f.includes("Screenshot") && !f.startsWith("P10") && !f.includes("blueprint")) + .slice(1, 4) + .map((f) => `/applications/${slug}/${f}`); + } + } catch (error) { + console.error(`[applications/${slug}] Image scan failed:`, error); } return { heroImage, blueprints, machines }; @@ -42,9 +53,8 @@ export async function generateMetadata({ }: { params: Promise<{ slug: string; locale: string }>; }): Promise { - const { slug, locale } = await params; - try { + const { slug, locale } = await params; const raw = await prisma.application.findUnique({ where: { slug } }); if (!raw) { return { @@ -53,18 +63,21 @@ export async function generateMetadata({ }; } - const data = getLocalizedData(raw, locale); + const data: any = getLocalizedData(raw, locale); const heroImage = getApplicationImages(slug).heroImage; + const title = data?.title || "Application"; + const description = data?.shortDescription || data?.subtitle || "FLUX RF industrial solutions."; return buildPageMetadata({ locale, pathWithoutLocale: `applications/${slug}`, - title: `${data.title} — RF Industrial Solutions | FLUX`, - description: data.shortDescription || data.subtitle, + title: `${title} — RF Industrial Solutions | FLUX`, + description, ogImageUrl: heroImage || undefined, type: "product", }); - } catch { + } catch (error) { + console.error("[applications generateMetadata]", error); return { title: "FLUX | Energy, Directed." }; } } @@ -110,7 +123,13 @@ export default async function ApplicationPage({ params }: { params: Promise<{ sl } // 🔥 TRADUCIMOS LA APLICACIÓN PRINCIPAL - const data = getLocalizedData(rawData, locale); + let data: any; + try { + data = getLocalizedData(rawData, locale); + } catch (error) { + console.error(`[applications/${slug}] Locale merge failed:`, error); + data = rawData; + } // 2. Buscamos el "Muro de Soluciones" (Casos Reales específicos de esta app) let rawRealCases: any[] = []; @@ -128,30 +147,44 @@ export default async function ApplicationPage({ params }: { params: Promise<{ sl } // 🔥 TRADUCIMOS TODOS LOS CASOS DE ESTUDIO DEL MURO - const realCases = rawRealCases.map((node: any) => getLocalizedData(node, locale)); + let realCases: any[] = []; + try { + realCases = rawRealCases.map((node: any) => getLocalizedData(node, locale)); + } catch (error) { + console.error(`[applications/${slug}] Cases locale merge failed:`, error); + realCases = rawRealCases; + } // 3. Leemos las imágenes de la carpeta original const images = getApplicationImages(slug); - const url = `${baseUrl()}/${locale}/applications/${slug}`; - const jsonLd = [ - productSchema({ - name: data.title, - description: data.shortDescription || data.subtitle, - imageUrl: images.heroImage || undefined, - category: data.category, - url, - }), - breadcrumbSchema([ - { name: "Home", url: `${baseUrl()}/${locale}` }, - { name: "Applications", url: `${baseUrl()}/${locale}#applications-deep` }, - { name: data.title, url }, - ]), - ]; + // 4. JSON-LD structured data — wrapped to never break the render. + let jsonLd: object[] = []; + try { + const url = `${baseUrl()}/${locale}/applications/${slug}`; + const title = data?.title || "FLUX Application"; + const description = data?.shortDescription || data?.subtitle || ""; + jsonLd = [ + productSchema({ + name: title, + description, + imageUrl: images.heroImage || undefined, + category: data?.category || "RF Industrial", + url, + }), + breadcrumbSchema([ + { name: "Home", url: `${baseUrl()}/${locale}` }, + { name: "Applications", url: `${baseUrl()}/${locale}#applications-deep` }, + { name: title, url }, + ]), + ]; + } catch (error) { + console.error(`[applications/${slug}] JSON-LD build failed:`, error); + } return ( <> - + {jsonLd.length > 0 && } ); diff --git a/src/app/[locale]/error.tsx b/src/app/[locale]/error.tsx new file mode 100644 index 0000000..072c51e --- /dev/null +++ b/src/app/[locale]/error.tsx @@ -0,0 +1,47 @@ +"use client"; + +// Locale-scoped error boundary — caught here so the root layout +// (NavBar, Footer, etc.) keeps rendering around the error UI. +// Useful for diagnosing per-page failures without losing site chrome. + +import { useEffect } from "react"; + +export default function LocaleError({ + error, + reset, +}: { + error: Error & { digest?: string }; + reset: () => void; +}) { + useEffect(() => { + console.error("[LocaleError]", error); + }, [error]); + + return ( +
+
+
+ Page error +
+

+ This page hit a problem. +

+

+ The site is up but this specific page failed to render. +

+ +
+          {error.message || "Unknown error"}
+          {error.digest ? `\n\nDigest: ${error.digest}` : ""}
+        
+ + +
+
+ ); +} diff --git a/src/app/[locale]/news/[slug]/page.tsx b/src/app/[locale]/news/[slug]/page.tsx index 122d5c9..1e94f1d 100644 --- a/src/app/[locale]/news/[slug]/page.tsx +++ b/src/app/[locale]/news/[slug]/page.tsx @@ -228,9 +228,12 @@ export default async function ArticlePage({ params }: { params: Promise<{ slug: const resolvedParams = await params; const { slug, locale } = resolvedParams; - const rawArticle = await prisma.newsArticle.findUnique({ - where: { slug } - }); + let rawArticle: any = null; + try { + rawArticle = await prisma.newsArticle.findUnique({ where: { slug } }); + } catch (error) { + console.error(`[news/${slug}] DB fetch failed:`, error); + } if (!rawArticle) { return ( @@ -242,32 +245,44 @@ export default async function ArticlePage({ params }: { params: Promise<{ slug: } // 🔥 TRADUCCIÓN MÁGICA ANTES DEL RENDER 🔥 - const article = getLocalizedData(rawArticle, locale); + let article: any; + try { + article = getLocalizedData(rawArticle, locale); + } catch (error) { + console.error(`[news/${slug}] Locale merge failed:`, error); + article = rawArticle; + } let gallery: string[] = []; try { gallery = JSON.parse(article.galleryJson || "[]"); } catch (e) {} const articleUrl = `${baseUrl()}/${locale}/news/${slug}`; const cover = article.coverImage ? `/news/${slug}/${article.coverImage}` : undefined; - const jsonLd = [ - articleSchema({ - headline: article.title, - description: article.excerpt, - imageUrl: cover, - url: articleUrl, - publishedAt: article.publishedAt, - updatedAt: article.updatedAt, - }), - breadcrumbSchema([ - { name: "Home", url: `${baseUrl()}/${locale}` }, - { name: "News", url: `${baseUrl()}/${locale}/news` }, - { name: article.title, url: articleUrl }, - ]), - ]; + + let jsonLd: object[] = []; + try { + jsonLd = [ + articleSchema({ + headline: article?.title || "FLUX Article", + description: article?.excerpt || "", + imageUrl: cover, + url: articleUrl, + publishedAt: article?.publishedAt || new Date(), + updatedAt: article?.updatedAt || new Date(), + }), + breadcrumbSchema([ + { name: "Home", url: `${baseUrl()}/${locale}` }, + { name: "News", url: `${baseUrl()}/${locale}/news` }, + { name: article?.title || "Article", url: articleUrl }, + ]), + ]; + } catch (error) { + console.error(`[news/${slug}] JSON-LD build failed:`, error); + } return (
- + {jsonLd.length > 0 && }
diff --git a/src/app/global-error.tsx b/src/app/global-error.tsx new file mode 100644 index 0000000..20da745 --- /dev/null +++ b/src/app/global-error.tsx @@ -0,0 +1,78 @@ +"use client"; + +// Global error boundary — catches errors that bubble up past route-level +// error.tsx files. Renders its own / because the root layout +// errored too. + +import { useEffect } from "react"; + +export default function GlobalError({ + error, + reset, +}: { + error: Error & { digest?: string }; + reset: () => void; +}) { + useEffect(() => { + console.error("[GlobalError]", error); + }, [error]); + + return ( + + +
+

+ Something went wrong on FLUX +

+

+ The page hit an unexpected error. The team has been notified. +

+ +
+            {error.message || "Unknown error"}
+            {error.digest ? `\n\nDigest: ${error.digest}` : ""}
+          
+ + +
+ + + ); +}