diff --git a/src/app/(main)/[lang]/page.tsx b/src/app/(main)/[lang]/page.tsx index 892e8749c..6c3d94d91 100644 --- a/src/app/(main)/[lang]/page.tsx +++ b/src/app/(main)/[lang]/page.tsx @@ -1,28 +1,19 @@ import { Metadata } from "next"; -import CustomErrorMessage from "src/blog/components/customErrorMessage/CustomErrorMessage"; import InformationSection from "src/blog/components/informationSection/InformationSection"; -import { homeLink } from "src/blog/components/utils/linkTypes"; import { getDraftModeInfo } from "src/utils/draftmode"; import SectionRenderer from "src/utils/renderSection"; -import { fetchSeoData, generateMetadataFromSeo } from "src/utils/seo"; -import { client } from "studio/lib/client"; +import { generateMetadataFromSeo } from "src/utils/seo"; import { LinkType } from "studio/lib/interfaces/navigation"; import { PageBuilder } from "studio/lib/interfaces/pages"; -import { LanguageObject } from "studio/lib/interfaces/supportedLanguages"; -import { PAGE_QUERY, PAGE_SEO_QUERY } from "studio/lib/queries/pages"; -import { - LANDING_PAGE_REF_QUERY, - LANGUAGES_QUERY, -} from "studio/lib/queries/siteSettings"; +import { LANDING_PAGE_QUERY } from "studio/lib/queries/siteSettings"; import { loadStudioQuery } from "studio/lib/store"; export async function generateMetadata(): Promise { - const { data: landingId } = await loadStudioQuery( - LANDING_PAGE_REF_QUERY, + const { data: landingPage } = await loadStudioQuery( + LANDING_PAGE_QUERY, ); - const seo = await fetchSeoData(PAGE_SEO_QUERY, { id: landingId }); - return generateMetadataFromSeo(seo); + return generateMetadataFromSeo(landingPage?.seo ?? null); } const navigationManagerLink = { @@ -33,44 +24,20 @@ const navigationManagerLink = { internalLink: { _ref: "studio/structure/navigationManager" }, }; -const pagesLink = { - _key: "go-to-pages", - _type: "link", - linkTitle: "Go to Pages Manager", - linkType: LinkType.Internal, - internalLink: { _ref: "studio/structure/pages" }, -}; - type Props = { params: { lang: string; slug: string }; }; -const Page404 = ( - -); - const Home = async ({ params }: Props) => { const { perspective, isDraftMode } = getDraftModeInfo(); - const language = ( - await client.fetch(LANGUAGES_QUERY) - )?.find((l) => l.id === params.lang); - - if (language === undefined) { - return Page404; - } - - const { data: landingId } = await loadStudioQuery( - LANDING_PAGE_REF_QUERY, - {}, + const initialLandingPage = await loadStudioQuery( + LANDING_PAGE_QUERY, + { language: params.lang }, { perspective }, ); - if (!landingId) { + if (initialLandingPage.data === null) { return ( { ); } - const initialLandingPage = await loadStudioQuery( - PAGE_QUERY, - { id: landingId, language: params.lang }, - { perspective }, - ); + const initialData = { + ...initialLandingPage, + data: initialLandingPage.data, + }; - if (!initialLandingPage.data) { - return ( - - ); - } - - return initialLandingPage.data.sections.map((section, index) => { - return ( - - ); - }); + return initialLandingPage.data.sections.map((section, index) => ( + + )); }; export default Home; diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 6e0b319bf..cc6fc8b75 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -16,7 +16,7 @@ const fontBrittiSans = localFont({ }); export async function generateMetadata(): Promise { - // TODO: Root metadata should only rely on seo from site settings + // fallback if no page-specific metadata is provided return generateMetadataFromSeo(null); } diff --git a/src/utils/seo.ts b/src/utils/seo.ts index b226e73c8..f0ecc9196 100644 --- a/src/utils/seo.ts +++ b/src/utils/seo.ts @@ -1,7 +1,4 @@ -import { toPlainText } from "@portabletext/toolkit"; -import type { QueryParams } from "@sanity/client"; import { Metadata } from "next"; -import { PortableTextBlock } from "sanity"; import { urlFor } from "studio/lib/image"; import { BrandAssets } from "studio/lib/interfaces/brandAssets"; @@ -21,55 +18,11 @@ export type SeoData = { keywords?: string; }; -type PostSeoData = { - title: string; - description: PortableTextBlock[]; - imageUrl: string; - keywords: string; -}; - export const OPEN_GRAPH_IMAGE_DIMENSIONS = { width: 1200, height: 630, }; -export async function fetchSeoData( - query: string, - variables?: QueryParams | undefined, -): Promise { - try { - const { data } = await loadStudioQuery(query, variables); - return data; - } catch (error) { - console.error("Error loading SEO data:", error); - return null; - } -} - -export async function fetchPostSeoData( - query: string, - variables?: QueryParams | undefined, -): Promise { - try { - const { data } = await loadStudioQuery(query, variables); - if (data && data.description) { - const plainTextDescription = toPlainText(data.description); - - return { - title: data.title, - description: plainTextDescription, - imageUrl: data.imageUrl, - keywords: data.keywords, - }; - } - - return null; - } catch (error) { - console.error("Error loading SEO data:", error); - return null; - } -} - export async function generateMetadataFromSeo( seo: SeoData | null, ): Promise { @@ -85,10 +38,10 @@ export async function generateMetadataFromSeo( const title = seo?.title || - defaultSeo?.seo?.seoTitle || + defaultSeo?.seo?.title || companyInfo?.companyName || "Variant"; - const description = seo?.description || defaultSeo?.seo?.seoDescription; + const description = seo?.description || defaultSeo?.seo?.description; const keywords = seo?.keywords || ""; const favicon = brandAssets?.favicon; @@ -102,7 +55,7 @@ export async function generateMetadataFromSeo( title: title, ...(description ? { description: description } : {}), })}`; - const sanityImageUrl = seo?.imageUrl || defaultSeo?.seo?.seoImageUrl; + const sanityImageUrl = seo?.imageUrl || defaultSeo?.seo?.imageUrl; const sanityImageParams = `?${new URLSearchParams({ w: OPEN_GRAPH_IMAGE_DIMENSIONS.width.toString(), h: OPEN_GRAPH_IMAGE_DIMENSIONS.height.toString(), diff --git a/studio/lib/interfaces/pages.ts b/studio/lib/interfaces/pages.ts index 8e37d54b8..d74d11176 100644 --- a/studio/lib/interfaces/pages.ts +++ b/studio/lib/interfaces/pages.ts @@ -1,5 +1,7 @@ import { PortableTextBlock } from "sanity"; +import { SeoData } from "src/utils/seo"; + import { Slug } from "./global"; import { IImage, ImageExtendedProps } from "./media"; import { ILink } from "./navigation"; @@ -103,6 +105,7 @@ export interface PageBuilder { page: string; sections: Section[]; slug: Slug; + seo: SeoData; } export interface SEO { diff --git a/studio/lib/interfaces/seo.ts b/studio/lib/interfaces/seo.ts index 59c5c4f12..267cf1ce3 100644 --- a/studio/lib/interfaces/seo.ts +++ b/studio/lib/interfaces/seo.ts @@ -1,9 +1,4 @@ -export interface SeoObject { - seoTitle?: string; - seoDescription?: string; - seoKeywords?: string; - seoImageUrl?: string; -} +import { SeoData } from "src/utils/seo"; export type DefaultSeo = { _id: string; @@ -11,5 +6,5 @@ export type DefaultSeo = { _createdAt: string; _updatedAt: string; _rev: string; - seo?: SeoObject; + seo?: SeoData; }; diff --git a/studio/lib/queries/pages.ts b/studio/lib/queries/pages.ts index 784bd691b..377a77dc6 100644 --- a/studio/lib/queries/pages.ts +++ b/studio/lib/queries/pages.ts @@ -1,6 +1,7 @@ import { groq } from "next-sanity"; import { LANGUAGE_FIELD_FRAGMENT, TRANSLATED_LINK_FRAGMENT } from "./i18n"; +import { translatedFieldFragment } from "./utils/i18n"; const SECTIONS_FRAGMENT = groq` sections[]{ @@ -36,10 +37,20 @@ const SECTIONS_FRAGMENT = groq` } `; +export const SEO_FRAGMENT = groq` + "seo": ${translatedFieldFragment("seo")} { + "title": seoTitle, + "description": seoDescription, + "imageUrl": seoImage.asset->url, + "keywords": seoKeywords + }, +`; + export const PAGE_FRAGMENT = groq` ..., ${LANGUAGE_FIELD_FRAGMENT}, - ${SECTIONS_FRAGMENT} + ${SECTIONS_FRAGMENT}, + ${SEO_FRAGMENT} `; export const PAGE_QUERY = groq` @@ -48,28 +59,12 @@ export const PAGE_QUERY = groq` } `; -export const PAGE_SEO_QUERY = groq` - *[_type == "pageBuilder" && _id == $id][0]{ - "title": seo.seoTitle, - "description": seo.seoDescription, - "imageUrl": seo.seoImage.asset->url - } -`; - export const PAGE_BY_SLUG_QUERY = groq` *[_type == "pageBuilder" && slug.current == $slug][0]{ ${PAGE_FRAGMENT} } `; -export const SEO_SLUG_QUERY = groq` - *[defined(seo) && slug.current == $slug][0]{ - "title": seo.seoTitle, - "description": seo.seoDescription, - "imageUrl": seo.seoImage.asset->url - } -`; - export const BLOG_PAGE_QUERY = groq` *[_type == "blog" && slug.current == $slug][0] `; @@ -90,14 +85,6 @@ export const POST_SLUG_QUERY = groq` *[_type == "blogPosts" && slug.current == $id][0] `; -export const SEO_POST_SLUG_QUERY = groq` - *[_type == "blogPosts" && slug.current == $id][0]{ - "title": basicTitle, - "description": richText, - "imageUrl": image.asset->url - } -`; - export const MORE_POST_PREVIEW = groq` *[_type == "blogPosts"] | order(_createdAt desc)[0..2] `; diff --git a/studio/lib/queries/siteSettings.ts b/studio/lib/queries/siteSettings.ts index ea7722032..a207f0cbf 100644 --- a/studio/lib/queries/siteSettings.ts +++ b/studio/lib/queries/siteSettings.ts @@ -5,7 +5,7 @@ import { TRANSLATED_LINK_FRAGMENT, TRANSLATED_SLUG_VALUE_FRAGMENT, } from "./i18n"; -import { PAGE_FRAGMENT } from "./pages"; +import { PAGE_FRAGMENT, SEO_FRAGMENT } from "./pages"; //Brand Assets export const BRAND_ASSETS_QUERY = groq` @@ -63,11 +63,7 @@ export const DEFAULT_LANGUAGE_QUERY = groq` //Default SEO export const DEFAULT_SEO_QUERY = groq` *[_type == "seoFallback"][0]{ - seo { - seoTitle, - seoDescription, - seoKeywords, - "seoImageUrl": seoImage.asset->url - } + ..., + ${SEO_FRAGMENT} } `;