From 297a960a1de1bc1c9ea564c4c2aa7bb038462d06 Mon Sep 17 00:00:00 2001 From: "Aman Kumar [SSW]" <71385247+amankumarrr@users.noreply.github.com> Date: Wed, 30 Oct 2024 09:54:08 +1100 Subject: [PATCH] App Routing - Migrating /events page (#3254) * App Routing - Migrating /events page Affected Route: `/event` Fixed #3164 * adding events/* pages * adding wrapper for queryprovider * removing unused imports * Adding react query hydration * adding the prefetch for the index page * removing the query provider from the layout * removing whitespace * caching the past events to improve the load time * removing unused formatCategory * removed unused default category props --------- Co-authored-by: Calinator444 --- app/events/[filename]/index.tsx | 111 +++++++++++++++++++ app/events/[filename]/page.tsx | 92 ++++++++++++++++ app/events/index.tsx | 46 ++++++++ app/events/page.tsx | 93 ++++++++++++++++ app/providers/query-provider.tsx | 29 +++++ components/filter/events.tsx | 3 - hooks/useFetchEvents.ts | 8 +- pages/events/[filename].tsx | 182 ------------------------------- pages/events/index.tsx | 109 ------------------ pnpm-lock.yaml | 19 ++-- 10 files changed, 385 insertions(+), 307 deletions(-) create mode 100644 app/events/[filename]/index.tsx create mode 100644 app/events/[filename]/page.tsx create mode 100644 app/events/index.tsx create mode 100644 app/events/page.tsx create mode 100644 app/providers/query-provider.tsx delete mode 100644 pages/events/[filename].tsx delete mode 100644 pages/events/index.tsx diff --git a/app/events/[filename]/index.tsx b/app/events/[filename]/index.tsx new file mode 100644 index 000000000..a481754f6 --- /dev/null +++ b/app/events/[filename]/index.tsx @@ -0,0 +1,111 @@ +"use client"; + +import { ClientLogos } from "@/components/blocks"; +import { Blocks } from "@/components/blocks-renderer"; +import { componentRenderer } from "@/components/blocks/mdxComponentRenderer"; +import EventsHeader from "@/components/events/eventsHeader"; +import { TestimonialRow } from "@/components/testimonials/TestimonialRow"; +import { Container } from "@/components/util/container"; +import { Section } from "@/components/util/section"; +import VideoCards from "@/components/util/videoCards"; +import { removeExtension } from "@/services/client/utils.service"; +import { Breadcrumbs } from "app/components/breadcrumb"; +import { tinaField } from "tinacms/dist/react"; +import { TinaMarkdown } from "tinacms/dist/rich-text"; + +export default function EventsPage({ props, tinaProps }) { + const { data } = tinaProps; + + const { categories } = props; + + const { videoCardProps } = props; + + return ( + <> +
+ +
+ + {data.events.showBreadcrumb && ( +
+ +
+ )} + {data.events.title && ( +

+ {data?.events?.title} +

+ )} + {data.events.subTitle && ( + +
+ +
+
+ )} + + + +
+ +
+ + {data.events.showTestimonials && ( +
+ +
+ +
+
+
+ )} + +
+ +
+

+ Trusted by more than 1000+{" "} + clients in the world +

+

+ Our software developers & consultants have delivered the best in + the business to more than 1,000 clients in 15 countries. +

+
+ +
+
+
+
+ +
+ + ); +} diff --git a/app/events/[filename]/page.tsx b/app/events/[filename]/page.tsx new file mode 100644 index 000000000..a229a3fda --- /dev/null +++ b/app/events/[filename]/page.tsx @@ -0,0 +1,92 @@ +import { VideoCardType } from "@/components/util/videoCards"; +import { getTestimonialsByCategories } 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 { TinaClient } from "../../tina-client"; +import EventsPages from "./index"; + +export const dynamicParams = false; + +export async function generateStaticParams() { + const pagesListData = await client.queries.eventsConnection(); + + const pages = pagesListData.data.eventsConnection.edges.map((page) => ({ + filename: page.node._sys.filename, + })); + + return pages; +} + +const getData = async (filename: string) => { + const tinaProps = await client.queries.eventsContentQuery({ + relativePath: `${filename}.mdx`, + date: TODAY.toISOString(), + }); + + const seo = tinaProps.data.events.seo; + + const categories = + tinaProps.data.events?.testimonialCategories?.map( + (category) => category.testimonialCategory.name + ) || []; + + const videoCardProps = + tinaProps.data?.events.videos?.videoCards?.map((m) => ({ + title: m.title, + link: m.link, + })) || []; + + const testimonialsResult = await getTestimonialsByCategories(categories); + + return { + props: { + data: tinaProps.data, + query: tinaProps.query, + variables: tinaProps.variables, + testimonialsResult, + categories, + videoCardProps, + 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 { + const tinaProps = await getData(params.filename); + + const seo = tinaProps.props.seo; + if (seo && !seo.canonical) { + seo.canonical = `${tinaProps.props.header.url}events/${params.filename}`; + } + + // eslint-disable-next-line react-hooks/rules-of-hooks + const { seoProps } = useSEO(seo); + + return { ...seoProps }; +} + +export default async function Events({ + params, +}: { + params: { filename: string }; +}) { + const { filename } = params; + + const { props } = await getData(filename); + + return ; +} diff --git a/app/events/index.tsx b/app/events/index.tsx new file mode 100644 index 000000000..e33dcfd9b --- /dev/null +++ b/app/events/index.tsx @@ -0,0 +1,46 @@ +"use client"; + +import { Blocks } from "@/components/blocks-renderer"; +import { componentRenderer } from "@/components/blocks/mdxComponentRenderer"; +import { EventsFilter } from "@/components/filter/events"; +import { Container } from "@/components/util/container"; +import { HydrationBoundary } from "@tanstack/react-query"; +import { QueryProvider } from "app/providers/query-provider"; +import { useSearchParams } from "next/navigation"; +import { TinaMarkdown } from "tinacms/dist/rich-text"; + +export default function EventsIndexPage({ props, tinaProps }) { + const { filterCategories } = props; + const { data } = tinaProps; + + const searchParams = useSearchParams(); + + const defaultToPastTab = searchParams.get("past") === "1"; + + return ( + + + +
+

SSW Events

+
+ +
+
+ +
+ +
+
+ ); +} diff --git a/app/events/page.tsx b/app/events/page.tsx new file mode 100644 index 000000000..c211c98e0 --- /dev/null +++ b/app/events/page.tsx @@ -0,0 +1,93 @@ +import client from "@/tina/client"; + +import { dehydrate, QueryClient } from "@tanstack/react-query"; +import { TinaClient } from "app/tina-client"; +import { + FUTURE_EVENTS_QUERY_KEY, + getEventsCategories, + getFutureEvents, + getPastEvents, + PAST_EVENTS_QUERY_KEY, + TODAY, +} from "hooks/useFetchEvents"; +import { useSEO } from "hooks/useSeo"; +import { Metadata } from "next"; +import EventIndex from "./index"; + +export const revalidate = 3600; + +const DEFAULT_FILTERS = "undefinedundefined"; + +export async function generateMetadata(): Promise { + const tinaProps = await getData(); + + const seo = tinaProps.props.seo; + if (seo && !seo.canonical) { + seo.canonical = `${tinaProps.props.header.url}events`; + } + + // eslint-disable-next-line react-hooks/rules-of-hooks + const { seoProps } = useSEO(seo); + + return { ...seoProps }; +} + +const getData = async () => { + const tinaProps = await client.queries.eventsIndexContentQuery({ + relativePath: "index.mdx", + date: TODAY.toISOString(), + }); + + const queryClient = new QueryClient(); + + const seo = tinaProps.data.eventsIndex.seo; + + const filterCategories = await getEventsCategories(); + + // Default filters are set to undefined on initial load to prevent losing the prefetched cache. + // This avoids extra load time on the client side. + const intialCachedQueryForFutureEvent = + FUTURE_EVENTS_QUERY_KEY + DEFAULT_FILTERS; + const intialCachedQueryForPastEvents = + PAST_EVENTS_QUERY_KEY + DEFAULT_FILTERS; + + await queryClient.prefetchInfiniteQuery({ + /* values of undefined cannot be serialized as JSON, so were passing the values as strings + using concatenation */ + queryKey: [intialCachedQueryForFutureEvent], + queryFn: () => getFutureEvents(), + initialPageParam: "", + }); + + await queryClient.prefetchInfiniteQuery({ + /* values of undefined cannot be serialized as JSON, so were passing the values as strings + using concatenation */ + queryKey: [intialCachedQueryForPastEvents], + queryFn: () => getPastEvents(), + initialPageParam: "", + }); + + if (!tinaProps.data.eventsIndex.seo.canonical) { + tinaProps.data.eventsIndex.seo.canonical = `${tinaProps.data.global.header.url}events`; + } + + return { + props: { + data: tinaProps.data, + query: tinaProps.query, + variables: tinaProps.variables, + filterCategories, + dehydratedState: dehydrate(queryClient), + header: { + url: tinaProps.data.global.header.url, + }, + seo, + }, + }; +}; + +export default async function EventPage() { + const { props } = await getData(); + + return ; +} diff --git a/app/providers/query-provider.tsx b/app/providers/query-provider.tsx new file mode 100644 index 000000000..b4833f3e8 --- /dev/null +++ b/app/providers/query-provider.tsx @@ -0,0 +1,29 @@ +"use client"; + +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; +import { ReactNode, useMemo } from "react"; + +const FIVE_MINS = 1000 * 60 * 5; + +function makeQueryClient() { + return new QueryClient({ + defaultOptions: { queries: { staleTime: FIVE_MINS } }, + }); +} + +export const QueryProvider = ({ children }: { children: ReactNode }) => { + const queryClient = useMemo(() => makeQueryClient(), []); + + return ( + + {children} + {process.env.NODE_ENV === "development" && ( + + )} + + ); +}; diff --git a/components/filter/events.tsx b/components/filter/events.tsx index eb2c25f5a..f4a8b448f 100644 --- a/components/filter/events.tsx +++ b/components/filter/events.tsx @@ -29,9 +29,6 @@ import { FilterGroupProps } from "./FilterGroup"; const EVENTS_JSON_LD_LIMIT = 5; -export const DEFAULT_TECHNOLOGY_FITLER = undefined; -export const DEFAULT_CATEGORY_FILTER = undefined; - interface EventsFilterProps { sidebarBody: TinaMarkdownContent; defaultToPastTab?: boolean; diff --git a/hooks/useFetchEvents.ts b/hooks/useFetchEvents.ts index 48f58dad0..2b77b284d 100644 --- a/hooks/useFetchEvents.ts +++ b/hooks/useFetchEvents.ts @@ -88,10 +88,10 @@ export const useFetchFutureEvents = (filters: SelectedCategories) => { }; }; -const getPastEvents = async ( - pageParam: string, - category: string = undefined, - calendarType: string = undefined +export const getPastEvents = async ( + pageParam?: string, + category?: string, + calendarType?: string ) => { const categories = getCategoriesForFilter(category); const res = await client.queries.getPastEventsQuery({ diff --git a/pages/events/[filename].tsx b/pages/events/[filename].tsx deleted file mode 100644 index 956b4c1d1..000000000 --- a/pages/events/[filename].tsx +++ /dev/null @@ -1,182 +0,0 @@ -import { tinaField, useTina } from "tinacms/dist/react"; - -import { client } from "@/tina/client"; -import { TODAY } from "hooks/useFetchEvents"; -import { InferGetStaticPropsType } from "next"; -import { TinaMarkdown } from "tinacms/dist/rich-text"; -import { ClientLogos } from "../../components/blocks"; -import { Blocks } from "../../components/blocks-renderer"; -import { Breadcrumbs } from "../../components/blocks/breadcrumbs"; -import { componentRenderer } from "../../components/blocks/mdxComponentRenderer"; -import EventsHeader from "../../components/events/eventsHeader"; -import { Layout } from "../../components/layout"; -import { TestimonialRow } from "../../components/testimonials/TestimonialRow"; -import { Container } from "../../components/util/container"; -import { Section } from "../../components/util/section"; -import { SEO } from "../../components/util/seo"; -import VideoCards, { VideoCardType } from "../../components/util/videoCards"; -import { RecaptchaContext } from "../../context/RecaptchaContext"; -import { getTestimonialsByCategories } from "../../helpers/getTestimonials"; -import { removeExtension } from "../../services/client/utils.service"; - -export default function EventsPage( - props: InferGetStaticPropsType -) { - const { data } = useTina({ - data: props.data, - query: props.query, - variables: props.variables, - }); - - const categories = - data.events.testimonialCategories - ?.filter((category) => !!category?.testimonialCategory) - .map((category) => category.testimonialCategory.name) ?? []; - - const videoCardProps = - data?.events.videos?.videoCards?.map((m) => ({ - title: m.title, - link: m.link, - })) || []; - - return ( - - - -
- -
- - {data.events.showBreadcrumb && ( -
- -
- )} - {data.events.title && ( -

- {data?.events?.title} -

- )} - {data.events.subTitle && ( - -
- -
-
- )} - - - -
- -
- - {data.events.showTestimonials && ( -
- -
- -
-
-
- )} - -
- -
-

- Trusted by more than{" "} - 1000+ clients in the - world -

-

- Our software developers & consultants have delivered the best - in the business to more than 1,000 clients in 15 countries. -

-
- -
-
-
-
- -
-
-
- ); -} - -export const getStaticProps = async ({ params }) => { - const tinaProps = await client.queries.eventsContentQuery({ - relativePath: `${params.filename}.mdx`, - date: TODAY.toISOString(), - }); - - const categories = - tinaProps.data.events?.testimonialCategories?.map( - (category) => category.testimonialCategory.name - ) || []; - - const testimonialsResult = await getTestimonialsByCategories(categories); - - if (tinaProps.data.events.seo && !tinaProps.data.events.seo.canonical) { - tinaProps.data.events.seo.canonical = `${tinaProps.data.global.header.url}events/${params.filename}`; - } - - return { - props: { - data: tinaProps.data, - query: tinaProps.query, - variables: tinaProps.variables, - testimonialResult: testimonialsResult || [], - env: { - GOOGLE_RECAPTCHA_SITE_KEY: - process.env.GOOGLE_RECAPTCHA_SITE_KEY || null, - }, - }, - }; -}; - -export const getStaticPaths = async () => { - const pagesListData = await client.queries.eventsConnection(); - return { - paths: pagesListData.data.eventsConnection.edges.map((page) => ({ - params: { filename: page.node._sys.filename }, - })), - fallback: false, - }; -}; diff --git a/pages/events/index.tsx b/pages/events/index.tsx deleted file mode 100644 index 5fd636942..000000000 --- a/pages/events/index.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import client from "@/tina/client"; -import { - dehydrate, - HydrationBoundary, - QueryClient, -} from "@tanstack/react-query"; -import type { InferGetStaticPropsType } from "next"; -import { useRouter } from "next/router"; -import { useTina } from "tinacms/dist/react"; -import { TinaMarkdown } from "tinacms/dist/rich-text"; -import { Blocks } from "../../components/blocks-renderer"; -import { componentRenderer } from "../../components/blocks/mdxComponentRenderer"; -import { - DEFAULT_CATEGORY_FILTER, - DEFAULT_TECHNOLOGY_FITLER, - EventsFilter, -} from "../../components/filter/events"; -import { Layout } from "../../components/layout"; -import { Container } from "../../components/util/container"; -import { SEO } from "../../components/util/seo"; -import { - FUTURE_EVENTS_QUERY_KEY, - getEventsCategories, - getFutureEvents, - TODAY, -} from "../../hooks/useFetchEvents"; - -const ISR_TIME = 60 * 60; // 1 hour - -export default function EventsIndexPage( - props: InferGetStaticPropsType -) { - const { query, variables, filterCategories } = props; - const { data } = useTina({ - data: props.data, - query, - variables, - }); - - const router = useRouter(); - const defaultToPastTab = - new URLSearchParams(router.asPath.split(/\?/)[1]).get("past") === "1"; - - return ( - - - - -
-

SSW Events

-
- -
-
- -
- -
-
- ); -} - -export const getStaticProps = async () => { - const tinaProps = await client.queries.eventsIndexContentQuery({ - relativePath: "index.mdx", - date: TODAY.toISOString(), - }); - - const queryClient = new QueryClient(); - - const filterCategories = await getEventsCategories(); - - await queryClient.prefetchInfiniteQuery({ - /* values of undefined cannot be serialized as JSON, so were passing the values as strings - using concatenation */ - queryKey: [ - FUTURE_EVENTS_QUERY_KEY + - DEFAULT_TECHNOLOGY_FITLER + - DEFAULT_CATEGORY_FILTER, - ], - queryFn: () => getFutureEvents(), - initialPageParam: "", - }); - - if (!tinaProps.data.eventsIndex.seo.canonical) { - tinaProps.data.eventsIndex.seo.canonical = `${tinaProps.data.global.header.url}events`; - } - - return { - props: { - data: tinaProps.data, - query: tinaProps.query, - variables: tinaProps.variables, - filterCategories, - dehydratedState: dehydrate(queryClient), - }, - revalidate: ISR_TIME, - }; -}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4a24e8f78..68200248e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1272,8 +1272,9 @@ packages: peerDependencies: graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - '@graphql-codegen/plugin-helpers@5.0.4': - resolution: {integrity: sha512-MOIuHFNWUnFnqVmiXtrI+4UziMTYrcquljaI5f/T/Bc7oO7sXcfkAvgkNWEEi9xWreYwvuer3VHCuPI/lAFWbw==} + '@graphql-codegen/plugin-helpers@5.1.0': + resolution: {integrity: sha512-Y7cwEAkprbTKzVIe436TIw4w03jorsMruvCvu0HJkavaKMQbWY+lQ1RIuROgszDbxAyM35twB5/sUvYG5oW+yg==} + engines: {node: '>=16'} peerDependencies: graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 @@ -9682,7 +9683,7 @@ snapshots: lodash: 4.17.21 tslib: 2.4.1 - '@graphql-codegen/plugin-helpers@5.0.4(graphql@15.8.0)': + '@graphql-codegen/plugin-helpers@5.1.0(graphql@15.8.0)': dependencies: '@graphql-tools/utils': 10.5.4(graphql@15.8.0) change-case-all: 1.0.15 @@ -9694,14 +9695,14 @@ snapshots: '@graphql-codegen/schema-ast@4.1.0(graphql@15.8.0)': dependencies: - '@graphql-codegen/plugin-helpers': 5.0.4(graphql@15.8.0) + '@graphql-codegen/plugin-helpers': 5.1.0(graphql@15.8.0) '@graphql-tools/utils': 10.5.4(graphql@15.8.0) graphql: 15.8.0 tslib: 2.6.3 '@graphql-codegen/typescript-operations@4.2.3(graphql@15.8.0)': dependencies: - '@graphql-codegen/plugin-helpers': 5.0.4(graphql@15.8.0) + '@graphql-codegen/plugin-helpers': 5.1.0(graphql@15.8.0) '@graphql-codegen/typescript': 4.0.9(graphql@15.8.0) '@graphql-codegen/visitor-plugin-common': 5.3.1(graphql@15.8.0) auto-bind: 4.0.0 @@ -9713,7 +9714,7 @@ snapshots: '@graphql-codegen/typescript@4.0.9(graphql@15.8.0)': dependencies: - '@graphql-codegen/plugin-helpers': 5.0.4(graphql@15.8.0) + '@graphql-codegen/plugin-helpers': 5.1.0(graphql@15.8.0) '@graphql-codegen/schema-ast': 4.1.0(graphql@15.8.0) '@graphql-codegen/visitor-plugin-common': 5.3.1(graphql@15.8.0) auto-bind: 4.0.0 @@ -9725,7 +9726,7 @@ snapshots: '@graphql-codegen/visitor-plugin-common@4.1.2(graphql@15.8.0)': dependencies: - '@graphql-codegen/plugin-helpers': 5.0.4(graphql@15.8.0) + '@graphql-codegen/plugin-helpers': 5.1.0(graphql@15.8.0) '@graphql-tools/optimize': 2.0.0(graphql@15.8.0) '@graphql-tools/relay-operation-optimizer': 7.0.1(graphql@15.8.0) '@graphql-tools/utils': 10.5.4(graphql@15.8.0) @@ -9742,7 +9743,7 @@ snapshots: '@graphql-codegen/visitor-plugin-common@5.3.1(graphql@15.8.0)': dependencies: - '@graphql-codegen/plugin-helpers': 5.0.4(graphql@15.8.0) + '@graphql-codegen/plugin-helpers': 5.1.0(graphql@15.8.0) '@graphql-tools/optimize': 2.0.0(graphql@15.8.0) '@graphql-tools/relay-operation-optimizer': 7.0.1(graphql@15.8.0) '@graphql-tools/utils': 10.5.4(graphql@15.8.0) @@ -11290,7 +11291,7 @@ snapshots: '@tinacms/cli@1.6.9(@codemirror/language@6.0.0)(@types/node@20.16.13)(@types/react@18.3.12)(abstract-level@1.0.4)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@3.29.5)(scheduler@0.23.2)(sucrase@3.35.0)(terser@5.31.6)(ts-node@10.9.2(@types/node@20.16.13)(typescript@5.6.3))': dependencies: '@graphql-codegen/core': 2.6.8(graphql@15.8.0) - '@graphql-codegen/plugin-helpers': 5.0.4(graphql@15.8.0) + '@graphql-codegen/plugin-helpers': 5.1.0(graphql@15.8.0) '@graphql-codegen/typescript': 4.0.9(graphql@15.8.0) '@graphql-codegen/typescript-operations': 4.2.3(graphql@15.8.0) '@graphql-codegen/visitor-plugin-common': 4.1.2(graphql@15.8.0)