From 847392de5de37f1d8f10138d71a63b8b35d89993 Mon Sep 17 00:00:00 2001 From: Mathias Oterhals Myklebust Date: Thu, 10 Oct 2024 14:02:04 +0200 Subject: [PATCH] feat: fetch page data based on catch-all route first step in supporting more advanced paths like nested slugs --- src/app/(main)/[lang]/[...path]/page.tsx | 114 ++++++++++ src/app/(main)/[lang]/[slug]/page.tsx | 180 ---------------- src/utils/pageData.ts | 247 ++++++++++++++++++++++ studioShared/lib/queries/customerCases.ts | 2 + 4 files changed, 363 insertions(+), 180 deletions(-) create mode 100644 src/app/(main)/[lang]/[...path]/page.tsx delete mode 100644 src/app/(main)/[lang]/[slug]/page.tsx create mode 100644 src/utils/pageData.ts diff --git a/src/app/(main)/[lang]/[...path]/page.tsx b/src/app/(main)/[lang]/[...path]/page.tsx new file mode 100644 index 000000000..ccbfae1cc --- /dev/null +++ b/src/app/(main)/[lang]/[...path]/page.tsx @@ -0,0 +1,114 @@ +import { Metadata } from "next"; + +import CustomErrorMessage from "src/blog/components/customErrorMessage/CustomErrorMessage"; +import Legal from "src/blog/components/legal/Legal"; +import LegalPreview from "src/blog/components/legal/LegalPreview"; +import { homeLink } from "src/blog/components/utils/linkTypes"; +import Compensations from "src/compensations/Compensations"; +import CompensationsPreview from "src/compensations/CompensationsPreview"; +import CustomerCases from "src/customerCases/CustomerCases"; +import CustomerCasesPreview from "src/customerCases/CustomerCasesPreview"; +import { getDraftModeInfo } from "src/utils/draftmode"; +import { fetchPageDataFromParams } from "src/utils/pageData"; +import SectionRenderer from "src/utils/renderSection"; +import { fetchSeoData, generateMetadataFromSeo } from "src/utils/seo"; +import { SEO_SLUG_QUERY } from "studio/lib/queries/pages"; + +export const dynamic = "force-dynamic"; + +type Props = { + params: { lang: string; path: string[] }; +}; + +function deprecated__slugFromPath(path: string[]) { + return path[0]; +} + +export async function generateMetadata({ params }: Props): Promise { + const seo = await fetchSeoData(SEO_SLUG_QUERY, { + slug: deprecated__slugFromPath(params.path), + }); + + return generateMetadataFromSeo(seo); +} + +const Page404 = ( + +); + +async function Page({ params }: Props) { + const { lang, path } = params; + + const { perspective, isDraftMode } = getDraftModeInfo(); + + const pageData = await fetchPageDataFromParams({ + language: lang, + path, + perspective: perspective ?? "published", + }); + + if (pageData == null) { + return Page404; + } + + const { queryResponse, docType } = pageData; + + switch (docType) { + case "pageBuilder": + return ( + <> + {queryResponse.data?.sections?.map((section, index) => ( + + ))} + + ); + case "compensations": + return isDraftMode ? ( + + ) : ( + + ); + case "customerCasesPage": + return isDraftMode ? ( + + ) : ( + + ); + case "customerCase": + return ( + // TODO: implement customer case detail page +
+          {JSON.stringify(pageData, null, 2)}
+        
+ ); + case "legalDocument": + return isDraftMode ? ( + + ) : ( + + ); + } + + return Page404; +} + +export default Page; diff --git a/src/app/(main)/[lang]/[slug]/page.tsx b/src/app/(main)/[lang]/[slug]/page.tsx deleted file mode 100644 index b9fc8271f..000000000 --- a/src/app/(main)/[lang]/[slug]/page.tsx +++ /dev/null @@ -1,180 +0,0 @@ -import { Metadata } from "next"; - -import { Blog } from "src/blog/Blog"; -import BlogPreview from "src/blog/BlogPreview"; -import CustomErrorMessage from "src/blog/components/customErrorMessage/CustomErrorMessage"; -import Legal from "src/blog/components/legal/Legal"; -import LegalPreview from "src/blog/components/legal/LegalPreview"; -import { homeLink } from "src/blog/components/utils/linkTypes"; -import Compensations from "src/compensations/Compensations"; -import CompensationsPreview from "src/compensations/CompensationsPreview"; -import CustomerCases from "src/customerCases/CustomerCases"; -import CustomerCasesPreview from "src/customerCases/CustomerCasesPreview"; -import { getDraftModeInfo } from "src/utils/draftmode"; -import SectionRenderer from "src/utils/renderSection"; -import { fetchSeoData, generateMetadataFromSeo } from "src/utils/seo"; -import { CompanyLocation } from "studio/lib/interfaces/companyDetails"; -import { CompensationsPage } from "studio/lib/interfaces/compensations"; -import { LegalDocument } from "studio/lib/interfaces/legalDocuments"; -import { LocaleDocument } from "studio/lib/interfaces/locale"; -import { BlogPage, PageBuilder, Post } from "studio/lib/interfaces/pages"; -import { CustomerCasePage } from "studio/lib/interfaces/specialPages"; -import { - COMPANY_LOCATIONS_QUERY, - LEGAL_DOCUMENT_BY_SLUG_AND_LANG_QUERY, -} from "studio/lib/queries/admin"; -import { LOCALE_QUERY } from "studio/lib/queries/locale"; -import { - BLOG_PAGE_QUERY, - PAGE_BY_SLUG_QUERY, - POSTS_QUERY, - SEO_SLUG_QUERY, -} from "studio/lib/queries/pages"; -import { - COMPENSATIONS_PAGE_QUERY, - CUSTOMER_CASES_PAGE_QUERY, -} from "studio/lib/queries/specialPages"; -import { loadStudioQuery } from "studio/lib/store"; - -export const dynamic = "force-dynamic"; - -type Props = { - params: { lang: string; slug: string }; -}; - -export async function generateMetadata({ params }: Props): Promise { - const seo = await fetchSeoData(SEO_SLUG_QUERY, { slug: params.slug }); - - return generateMetadataFromSeo(seo); -} - -const Page404 = ( - -); - -async function Page({ params }: Props) { - const { lang, slug } = params; - - const { perspective, isDraftMode } = getDraftModeInfo(); - - const [ - initialPage, - initialBlogPage, - initialCompensationsPage, - initialLocationsData, - initialCustomerCases, - initialLegalDocument, - initialLocale, - ] = await Promise.all([ - loadStudioQuery( - PAGE_BY_SLUG_QUERY, - { slug, language: lang }, - { perspective }, - ), - loadStudioQuery(BLOG_PAGE_QUERY, { slug }, { perspective }), - loadStudioQuery( - COMPENSATIONS_PAGE_QUERY, - { slug, language: lang }, - { perspective }, - ), - loadStudioQuery( - COMPANY_LOCATIONS_QUERY, - {}, - { perspective }, - ), - loadStudioQuery( - CUSTOMER_CASES_PAGE_QUERY, - { slug }, - { perspective }, - ), - loadStudioQuery( - LEGAL_DOCUMENT_BY_SLUG_AND_LANG_QUERY, - { slug, language: lang }, - { perspective }, - ), - loadStudioQuery(LOCALE_QUERY, {}, { perspective }), - ]); - - if (initialPage.data) { - return ( - <> - {initialPage.data?.sections?.map((section, index) => ( - - ))} - - ); - } - - if (initialBlogPage.data) { - const initialPosts = await loadStudioQuery( - POSTS_QUERY, - { slug }, - { perspective }, - ); - - if (!initialPosts) { - return Page404; - } - - return isDraftMode ? ( - - ) : ( - - ); - } - - if (initialCompensationsPage.data && initialLocationsData.data) { - return isDraftMode ? ( - - ) : ( - - ); - } - - if (initialCustomerCases.data) { - return isDraftMode ? ( - - ) : ( - - ); - } - - if (initialLegalDocument.data) { - return isDraftMode ? ( - - ) : ( - - ); - } - - return Page404; -} - -export default Page; diff --git a/src/utils/pageData.ts b/src/utils/pageData.ts new file mode 100644 index 000000000..d29653cef --- /dev/null +++ b/src/utils/pageData.ts @@ -0,0 +1,247 @@ +import { ClientPerspective, QueryParams } from "@sanity/client"; +import { QueryResponseInitial } from "@sanity/react-loader"; + +import { CompanyLocation } from "studio/lib/interfaces/companyDetails"; +import { CompensationsPage } from "studio/lib/interfaces/compensations"; +import { LegalDocument } from "studio/lib/interfaces/legalDocuments"; +import { LocaleDocument } from "studio/lib/interfaces/locale"; +import { PageBuilder } from "studio/lib/interfaces/pages"; +import { CustomerCasePage } from "studio/lib/interfaces/specialPages"; +import { + COMPANY_LOCATIONS_QUERY, + LEGAL_DOCUMENT_BY_SLUG_AND_LANG_QUERY, +} from "studio/lib/queries/admin"; +import { LOCALE_QUERY } from "studio/lib/queries/locale"; +import { PAGE_BY_SLUG_QUERY } from "studio/lib/queries/pages"; +import { + COMPENSATIONS_PAGE_QUERY, + CUSTOMER_CASES_PAGE_QUERY, +} from "studio/lib/queries/specialPages"; +import { loadStudioQuery } from "studio/lib/store"; +import { legalDocumentID } from "studio/schemas/documents/admin/legalDocuments"; +import { compensationsId } from "studio/schemas/documents/compensations"; +import { pageBuilderID } from "studio/schemas/documents/pageBuilder"; +import { customerCasesPageID } from "studio/schemas/documents/specialPages/customerCasesPage"; +import { CustomerCase } from "studioShared/lib/interfaces/customerCases"; +import { CUSTOMER_CASE_QUERY } from "studioShared/lib/queries/customerCases"; +import { loadSharedQuery } from "studioShared/lib/store"; +import { customerCaseID } from "studioShared/schemas/documents/customerCase"; + +type PageFromParams = { + query: string; + queryParams: QueryParams; + queryResponse: D; + docType: T; +}; + +async function fetchDynamicPage({ + language, + path, + perspective, +}: PageDataParams): Promise, + "pageBuilder" +> | null> { + if (path.length === 0) { + return null; + } + const queryParams = { + slug: path[0], + language, + }; + const pageResult = await loadStudioQuery( + PAGE_BY_SLUG_QUERY, + queryParams, + { perspective }, + ); + if (pageResult.data === null) { + return null; + } + return { + query: PAGE_BY_SLUG_QUERY, + queryParams, + queryResponse: { + ...pageResult, + data: pageResult.data, + }, + docType: pageBuilderID, + }; +} + +async function fetchCompensationsPage({ + language, + path, + perspective, +}: PageDataParams): Promise; + companyLocations: QueryResponseInitial; + locale: QueryResponseInitial; + }, + "compensations" +> | null> { + if (path.length !== 1) { + return null; + } + const queryParams = { + slug: path[0], + language, + }; + const compensationsPageResult = + await loadStudioQuery( + COMPENSATIONS_PAGE_QUERY, + queryParams, + { + perspective, + }, + ); + if (compensationsPageResult.data === null) { + return null; + } + const companyLocationsResult = await loadStudioQuery( + COMPANY_LOCATIONS_QUERY, + {}, + { perspective }, + ); + if (companyLocationsResult.data === null) { + return null; + } + const localeDocumentResult = await loadStudioQuery( + LOCALE_QUERY, + {}, + { perspective }, + ); + if (localeDocumentResult.data === null) { + return null; + } + return { + query: COMPENSATIONS_PAGE_QUERY, + queryParams, + queryResponse: { + compensationsPage: { + ...compensationsPageResult, + data: compensationsPageResult.data, + }, + companyLocations: { + ...companyLocationsResult, + data: companyLocationsResult.data, + }, + locale: { + ...localeDocumentResult, + data: localeDocumentResult.data, + }, + }, + docType: compensationsId, + }; +} + +async function fetchCustomerCase({ + language, + path, + perspective, +}: PageDataParams): Promise< + | PageFromParams, "customerCasesPage"> + | PageFromParams, "customerCase"> + | null +> { + if (path.length === 0) { + return null; + } + const customerCasesPageParams = { + slug: path[0], + }; + const customerCasesPageResult = + await loadStudioQuery( + CUSTOMER_CASES_PAGE_QUERY, + customerCasesPageParams, + { perspective }, + ); + if (customerCasesPageResult.data === null) { + return null; + } + if (path.length === 1) { + return { + query: CUSTOMER_CASES_PAGE_QUERY, + queryParams: customerCasesPageParams, + queryResponse: { + ...customerCasesPageResult, + data: customerCasesPageResult.data, + }, + docType: customerCasesPageID, + }; + } + const customerCaseParams = { + slug: path[1], + language, + }; + const customerCaseResult = await loadSharedQuery( + CUSTOMER_CASE_QUERY, + customerCaseParams, + { + perspective, + }, + ); + if (customerCaseResult.data === null) { + return null; + } + return { + query: CUSTOMER_CASE_QUERY, + queryParams: customerCaseParams, + queryResponse: { + ...customerCaseResult, + data: customerCaseResult.data, + }, + docType: customerCaseID, + }; +} + +async function fetchLegalDocument({ + language, + path, + perspective, +}: PageDataParams): Promise, + "legalDocument" +> | null> { + if (path.length !== 1) { + return null; + } + const queryParams = { + slug: path[0], + language, + }; + const queryResponse = await loadStudioQuery( + LEGAL_DOCUMENT_BY_SLUG_AND_LANG_QUERY, + queryParams, + { + perspective, + }, + ); + if (queryResponse.data === null) { + return null; + } + return { + query: LEGAL_DOCUMENT_BY_SLUG_AND_LANG_QUERY, + queryParams, + queryResponse: { + ...queryResponse, + data: queryResponse.data, + }, + docType: legalDocumentID, + }; +} + +export interface PageDataParams { + language: string; + path: string[]; + perspective: ClientPerspective; +} + +export async function fetchPageDataFromParams(params: PageDataParams) { + return ( + (await fetchDynamicPage(params)) ?? + (await fetchCompensationsPage(params)) ?? + (await fetchCustomerCase(params)) ?? + (await fetchLegalDocument(params)) + ); +} diff --git a/studioShared/lib/queries/customerCases.ts b/studioShared/lib/queries/customerCases.ts index c9b243d98..271ed9eb5 100644 --- a/studioShared/lib/queries/customerCases.ts +++ b/studioShared/lib/queries/customerCases.ts @@ -2,3 +2,5 @@ import { groq } from "next-sanity"; export const CUSTOMER_CASES_QUERY = groq`*[_type == "customerCase" && language == $language] `; + +export const CUSTOMER_CASE_QUERY = groq`*[_type == "customerCase" && slug.current == $slug && language == $language][0]`;