-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
App Routing - Consulting page (#3209)
* App Routing - Consulting page Affected Route: `/consulting` Fixed: #3162 Screenshot: TODO * Adding animation for the consulting page * Fixing the static path for the consulting pages * Optimizing it for generic component * removing unused prop from the server component * Fixing Tina editing with the template * Adding marketing props * removing unused component * removing client side rendering for tech cards * Adding categories from the server side * adding mediaCardsProps from server * removing unused component import * creating a generic component for props and tinaProp * testing - removing useEffect for the animation * Revert "testing - removing useEffect for the animation" This reverts commit d63919b. * Testing without prefetch * Revert "Testing without prefetch" This reverts commit 192616e. * removing the additional font weight from the parent component * Fixing the layout shifting issue with the banner * fixing the placeholder for breadcrumb * Adding video on the client size after the load * Moving Video Embed on the client side * loading the chatbot on the pure client side * Revert "loading the chatbot on the pure client side" This reverts commit 9197095. * Adding video back to component
- Loading branch information
1 parent
27ada7b
commit 64c8bad
Showing
8 changed files
with
360 additions
and
293 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
"use client"; | ||
|
||
import { BookingButton, BuiltOnAzure, ClientLogos } from "@/components/blocks"; | ||
import { Blocks } from "@/components/blocks-renderer"; | ||
import { Booking } from "@/components/blocks/booking"; | ||
import { componentRenderer } from "@/components/blocks/mdxComponentRenderer"; | ||
import { CallToAction } from "@/components/callToAction/callToAction"; | ||
import MediaCards from "@/components/consulting/mediaCard/mediaCards"; | ||
import { Marketing } from "@/components/marketing/Marketing"; | ||
import TechnologyCards from "@/components/technologyCard/technologyCards"; | ||
import { TestimonialRow } from "@/components/testimonials/TestimonialRow"; | ||
import { Benefits } from "@/components/util/consulting/benefits"; | ||
import { Container } from "@/components/util/container"; | ||
import { Section } from "@/components/util/section"; | ||
import { sanitiseXSS, spanWhitelist } from "@/helpers/validator"; | ||
import { removeExtension } from "@/services/client/utils.service"; | ||
import { Breadcrumbs } from "app/components/breadcrumb"; | ||
|
||
import { ReactElement } from "react"; | ||
import ReactDOMServer from "react-dom/server"; | ||
import { tinaField } from "tinacms/dist/react"; | ||
import { TinaMarkdown } from "tinacms/dist/rich-text"; | ||
|
||
export default function Consulting({ tinaProps, props }) { | ||
const { techCards, marketingData, categories, mediaCardProps } = props; | ||
const { data } = tinaProps; | ||
return ( | ||
<> | ||
<Section className="mx-auto min-h-24 w-full max-w-9xl px-8 py-5 md:min-h-16"> | ||
<Breadcrumbs | ||
path={removeExtension(props.variables.relativePath)} | ||
suffix={data.global.breadcrumbSuffix} | ||
title={data.consulting.seo?.title} | ||
seoSchema={data.consulting.seo} | ||
/> | ||
</Section> | ||
<Section className="w-full" color="black"> | ||
<Booking {...data.consulting.booking}> | ||
<BookingButton /> | ||
</Booking> | ||
</Section> | ||
<Section | ||
color="black" | ||
className={"prose-dark border-y-4 border-y-sswRed text-center"} | ||
> | ||
<a id="more" /> | ||
<div className="w-full bg-benefits bg-cover bg-fixed bg-center bg-no-repeat py-12"> | ||
<div | ||
data-tina-field={tinaField(data.consulting, "_body")} | ||
className="mx-auto max-w-9xl px-4" | ||
> | ||
<TinaMarkdown | ||
components={componentRenderer} | ||
content={data.consulting._body} | ||
/> | ||
<Benefits data={data.consulting.benefits} /> | ||
</div> | ||
</div> | ||
</Section> | ||
<Section className="mb-16"> | ||
<Container padding="px-4" className="flex w-full flex-wrap"> | ||
{data.consulting.afterBody ? ( | ||
<div> | ||
<Blocks | ||
prefix={"ConsultingAfterBody"} | ||
blocks={data.consulting.afterBody} | ||
/> | ||
</div> | ||
) : ( | ||
<></> | ||
)} | ||
<TestimonialRow | ||
testimonialsResult={props.testimonialsResult} | ||
categories={categories} | ||
tagline={data.consulting.testimonials?.tagline} | ||
/> | ||
<BookingButton | ||
data={{ | ||
containerClass: "mt-20", | ||
}} | ||
/> | ||
</Container> | ||
</Section> | ||
<Marketing content={marketingData} /> | ||
<Section className="!bg-gray-75 pb-40"> | ||
<Container size="custom"> | ||
<h1 className="text-center">Companies we have worked with</h1> | ||
<ClientLogos /> | ||
</Container> | ||
</Section> | ||
{!!techCards.length && ( | ||
<Section className="pb-16 text-center"> | ||
<Container padding="px-4"> | ||
<TechnologyCards {...data.consulting.technologies} /> | ||
</Container> | ||
</Section> | ||
)} | ||
{!!mediaCardProps.length && ( | ||
<Section className="pb-40 pt-8 text-center"> | ||
<Container size="custom"> | ||
<MediaCards | ||
header={data.consulting.medias?.header} | ||
cardProps={mediaCardProps} | ||
/> | ||
</Container> | ||
</Section> | ||
)} | ||
{data?.consulting?.callToAction?.showCallToAction && ( | ||
<CallToAction | ||
buttonSubtitle={data?.consulting?.callToAction?.buttonSubtitle} | ||
subTitle={data?.consulting?.callToAction?.subTitle} | ||
animated={data?.consulting?.callToAction?.animated} | ||
buttonText={data?.consulting?.callToAction?.buttonText} | ||
tinaFields={{ | ||
subTitle: tinaField(data.consulting?.callToAction, "subTitle"), | ||
buttonSubtitle: tinaField( | ||
data.consulting?.callToAction, | ||
"buttonSubtitle" | ||
), | ||
}} | ||
> | ||
<h2 | ||
className="callToAction" | ||
data-tina-field={tinaField(data.consulting.callToAction, "title")} | ||
dangerouslySetInnerHTML={{ | ||
__html: parseCallToAction( | ||
data.consulting?.callToAction?.title, | ||
data.consulting?.solution?.project, | ||
data.consulting?.solution | ||
), | ||
}} | ||
></h2> | ||
</CallToAction> | ||
)} | ||
|
||
<Section> | ||
<BuiltOnAzure data={{ backgroundColor: "default" }} /> | ||
</Section> | ||
</> | ||
); | ||
} | ||
|
||
const parseCallToAction = ( | ||
content: string, | ||
project: string, | ||
data: { project?: string } | ||
) => { | ||
const HTMLelement: ReactElement = ( | ||
<span | ||
className="text-sswRed" | ||
{...(data ? { "data-tina-field": tinaField(data, "project") } : {})} | ||
> | ||
{project} | ||
</span> | ||
); | ||
|
||
return sanitiseXSS( | ||
content?.replace("{{TITLE}}", ReactDOMServer.renderToString(HTMLelement)), | ||
spanWhitelist | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
import { MediaCardProps } from "@/components/consulting/mediaCard/mediaCard"; | ||
import { getRandomTestimonialsByCategory } from "@/helpers/getTestimonials"; | ||
import client from "@/tina/client"; | ||
import "aos/dist/aos.css"; // This is important to keep the animation | ||
import { TODAY } from "hooks/useFetchEvents"; | ||
import { useSEO } from "hooks/useSeo"; | ||
import { Metadata } from "next"; | ||
import { Open_Sans } from "next/font/google"; | ||
import { TinaClient } from "../../tina-client"; | ||
import ConsultingPage from "./consulting"; | ||
|
||
const openSans = Open_Sans({ | ||
variable: "--open-sans-font", | ||
subsets: ["latin"], | ||
display: "swap", | ||
weight: ["300", "400", "600"], | ||
}); | ||
|
||
export const dynamicParams = false; | ||
|
||
export async function generateStaticParams() { | ||
let pageListData = await client.queries.consultingConnection(); | ||
const allPagesListData = pageListData; | ||
|
||
while (pageListData.data.consultingConnection.pageInfo.hasNextPage) { | ||
const lastCursor = | ||
pageListData.data.consultingConnection.pageInfo.endCursor; | ||
pageListData = await client.queries.consultingConnection({ | ||
after: lastCursor, | ||
}); | ||
|
||
allPagesListData.data.consultingConnection.edges.push( | ||
...pageListData.data.consultingConnection.edges | ||
); | ||
} | ||
|
||
const pages = allPagesListData.data.consultingConnection.edges.map( | ||
(page) => ({ | ||
filename: page.node._sys.filename, | ||
}) | ||
); | ||
|
||
return pages; | ||
} | ||
|
||
const getData = async (filename: string) => { | ||
const tinaProps = await client.queries.consultingContentQuery({ | ||
relativePath: `${filename}.mdx`, | ||
date: TODAY.toISOString(), | ||
}); | ||
|
||
const seo = tinaProps.data.consulting.seo; | ||
|
||
const categories = | ||
tinaProps.data.consulting?.testimonialCategories | ||
?.map((category) => category?.testimonialCategory?.name) | ||
?.filter((item) => !!item) || []; | ||
|
||
const testimonialsResult = await getRandomTestimonialsByCategory(categories); | ||
|
||
const technologyCardNames = | ||
tinaProps.data.consulting.technologies?.technologyCards?.reduce<string[]>( | ||
(pre, cur) => { | ||
!!cur.technologyCard?.name && pre.push(cur.technologyCard.name); | ||
return pre; | ||
}, | ||
[] | ||
) || []; | ||
const technologyCardsProps = await client.queries.technologyCardContentQuery({ | ||
cardNames: technologyCardNames, | ||
}); | ||
|
||
const technologyCardDocs = | ||
technologyCardsProps?.data.technologiesConnection.edges.map((n) => n.node); | ||
const techCards = | ||
tinaProps.data.consulting.technologies?.technologyCards?.map((c) => ({ | ||
...technologyCardDocs?.find( | ||
(n) => !!n.name && n.name === c.technologyCard?.name | ||
), | ||
})) || []; | ||
|
||
const categoriesData = | ||
tinaProps.data.consulting.testimonialCategories | ||
?.filter((category) => !!category?.testimonialCategory) | ||
?.map((category) => category.testimonialCategory.name) ?? []; | ||
|
||
const mediaCardProps = | ||
tinaProps.data.consulting?.medias?.mediaCards?.map( | ||
(m): MediaCardProps => ({ | ||
type: m.type as MediaCardProps["type"], | ||
content: m.content, | ||
}) | ||
) || []; | ||
const marketingSection = await client.queries.marketing({ | ||
relativePath: "/why-choose-ssw.mdx", | ||
}); | ||
|
||
return { | ||
props: { | ||
data: tinaProps.data, | ||
query: tinaProps.query, | ||
variables: tinaProps.variables, | ||
testimonialsResult, | ||
techCards: techCards, | ||
marketingData: marketingSection.data, | ||
categories: categoriesData, | ||
mediaCardProps: mediaCardProps, | ||
header: { | ||
url: tinaProps.data.global.header.url, | ||
}, | ||
seo, | ||
...tinaProps, | ||
}, | ||
}; | ||
}; | ||
|
||
type GenerateMetaDataProps = { | ||
params: { filename: string }; | ||
searchParams: { [key: string]: string | string[] | undefined }; | ||
}; | ||
|
||
export async function generateMetadata({ | ||
params, | ||
}: GenerateMetaDataProps): Promise<Metadata> { | ||
const tinaProps = await getData(params.filename); | ||
|
||
const seo = tinaProps.props.seo; | ||
if (seo && !seo.canonical) { | ||
seo.canonical = `${tinaProps.props.header.url}consulting/${params.filename}`; | ||
} | ||
|
||
// eslint-disable-next-line react-hooks/rules-of-hooks | ||
const { seoProps } = useSEO(seo); | ||
|
||
return { ...seoProps }; | ||
} | ||
|
||
export default async function Consulting({ | ||
params, | ||
}: { | ||
params: { filename: string }; | ||
}) { | ||
const { filename } = params; | ||
|
||
const { props } = await getData(filename); | ||
|
||
return ( | ||
<div className={openSans.className}> | ||
<TinaClient props={props} Component={ConsultingPage} /> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.