Skip to content

Commit

Permalink
App Routing - Consulting page (#3209)
Browse files Browse the repository at this point in the history
* 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
amankumarrr authored Oct 17, 2024
1 parent 27ada7b commit 64c8bad
Show file tree
Hide file tree
Showing 8 changed files with 360 additions and 293 deletions.
161 changes: 161 additions & 0 deletions app/consulting/[filename]/consulting.tsx
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
);
};
152 changes: 152 additions & 0 deletions app/consulting/[filename]/page.tsx
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>
);
}
3 changes: 1 addition & 2 deletions app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,5 @@ export default async function HomePage({
}) {
const { filename } = params;
const tinaProps = await getData(filename);
const buildTime = new Date().toLocaleString();
return <PageClient props={{ ...tinaProps, buildTime }} />;
return <PageClient props={{ ...tinaProps }} />;
}
20 changes: 6 additions & 14 deletions components/blocks/booking.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { tinaField } from "tinacms/dist/react";
import { sanitiseXSS, spanWhitelist } from "../../helpers/validator";
import { CustomLink } from "../customLink";
import { Container } from "../util/container";
import VideoBackground from "./videoBackground";

export const Booking: FC<{
title?: string;
Expand Down Expand Up @@ -33,20 +34,11 @@ export const Booking: FC<{
</div>
</Container>

<video
className="absolute h-full min-w-full object-cover transition-opacity duration-1000 z-bgVideo"
playsInline
autoPlay
muted
loop
>
<source
data-tina-field={tinaField(props, "videoBackground")}
src={props.videoBackground}
type="video/mp4"
/>
Your browser does not support HTML5 video.
</video>
<VideoBackground
videoBackground={props.videoBackground}
tinaField={tinaField}
props={props}
/>
</div>
);
};
6 changes: 4 additions & 2 deletions components/blocks/mdxComponentRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,11 @@ const VerticalImageLayout = dynamic(() =>
const VerticalListItem = dynamic(() =>
import("./verticalListItem").then((mod) => mod.VerticalListItem)
);
const VideoEmbed = dynamic(() =>
import("./videoEmbed").then((mod) => mod.VideoEmbed)
const VideoEmbed = dynamic(
() => import("./videoEmbed").then((mod) => mod.VideoEmbed),
{ ssr: false }
);

const YoutubePlaylistBlock = dynamic<YoutubePlaylistProps>(() =>
import("./youtubePlaylist").then((mod) => mod.YoutubePlaylistBlock)
);
Expand Down
Loading

0 comments on commit 64c8bad

Please sign in to comment.