Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SUL23-491: Make global message a client-side component #167

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions app/api/global-message/route.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {NextRequest, NextResponse} from "next/server"
import {buildHeaders} from "@/lib/drupal/utils"
import {GraphQLClient} from "graphql-request"
import {getSdk} from "@/lib/gql/__generated__/queries"
import type {RequestConfig} from "graphql-request/src/types"

export const GET = async (request: NextRequest) => {
const graphqlClient = (requestConfig: RequestConfig = {}, isPreviewMode?: boolean) => {
requestConfig.headers = buildHeaders(requestConfig.headers as HeadersInit, isPreviewMode)
const client = new GraphQLClient(process.env.NEXT_PUBLIC_DRUPAL_BASE_URL + "/graphql", {
...requestConfig,
next: {
revalidate: 5, // revalidate every 5 seconds to catch updates as soon as possible.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that this is the right number here; there may be a better balance between BE requests and performance. I just set it to 5 sec for testing purposes.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This definitely is not good. This will cause every single page load to fetch every bit of data from Drupal. Resulting in millions of requests and slow page loads.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can I narrow this down so it only gets the config page for the global message instead of everything?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That will still increase requests for nearly every single page every user visits because it will fetch new config page data every 5 seconds.

...requestConfig.next,
},
fetch: async (input: URL | RequestInfo, init?: RequestInit) => fetch(input, init),
})
return getSdk(client)
}

try {
const headers = await buildHeaders()
const query = await graphqlClient({headers, next: {tags: ["config-pages"]}}).ConfigPages()
return NextResponse.json(query.stanfordGlobalMessages.nodes[0])
} catch (e) {
console.error("Failed to fetch global message", e)
}
}
83 changes: 42 additions & 41 deletions src/components/layout/global-message.tsx
Original file line number Diff line number Diff line change
@@ -1,91 +1,92 @@
import formatHtml from "@/lib/format-html";
import {DrupalActionLink} from "@/components/patterns/link";
import {
BellIcon,
CheckCircleIcon,
ExclamationCircleIcon,
ExclamationTriangleIcon,
InformationCircleIcon
} from "@heroicons/react/20/solid";
import {getConfigPage} from "@/lib/gql/fetcher";
import {StanfordGlobalMessage} from "@/lib/gql/__generated__/drupal.d";
import {JSX} from "react";
"use client"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do this on client, not the server.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be no reason for a client component or fetching from the client.

const GlobalMessage = async () => {
let configPage: StanfordGlobalMessage | undefined;
try {
configPage = await getConfigPage<StanfordGlobalMessage>("StanfordGlobalMessage")
} catch (e) {
return null;
}
import {useEffect, useState} from "react"
import formatHtml from "@/lib/format-html"
import {DrupalActionLink} from "@/components/patterns/link"
import {BellIcon, CheckCircleIcon, ExclamationCircleIcon, ExclamationTriangleIcon, InformationCircleIcon} from "@heroicons/react/20/solid"
import {StanfordGlobalMessage} from "@/lib/gql/__generated__/drupal.d"
import {JSX} from "react"

const GlobalMessage = () => {
const [configPage, setConfigPage] = useState<StanfordGlobalMessage | undefined>(undefined)

useEffect(() => {
const fetchConfigPage = async () => {
try {
const response = await fetch("/api/global-message")
const data = await response.json()
setConfigPage(data)
} catch (e) {
console.error("Failed to fetch global message", e)
}
}

fetchConfigPage()
}, [])

if (!configPage || !configPage.suGlobalMsgEnabled) {
return null;
return null
}

const options: Record<StanfordGlobalMessage["suGlobalMsgType"], { bgColor: string, textColor: string, linkClasses: string, icon: JSX.Element }> = {
const options: Record<StanfordGlobalMessage["suGlobalMsgType"], {bgColor: string; textColor: string; linkClasses: string; icon: JSX.Element}> = {
plain: {
bgColor: "bg-foggy-light",
textColor: "text-black-true",
linkClasses: "transition text-black-true hocus:text-black hocus:bg-sky",
icon: <BellIcon width={30}/>
icon: <BellIcon width={30} />,
},
success: {
bgColor: "bg-digital-green",
textColor: "text-white",
linkClasses: "transition text-white hocus:text-black hocus:bg-white",
icon: <CheckCircleIcon width={30}/>
icon: <CheckCircleIcon width={30} />,
},
info: {
bgColor: "bg-digital-blue-dark",
textColor: "text-white",
linkClasses: "transition text-white hocus:text-black hocus:bg-white",
icon: <InformationCircleIcon width={30}/>
icon: <InformationCircleIcon width={30} />,
},
warning: {
bgColor: "bg-illuminating-dark",
textColor: "text-black-true",
linkClasses: "transition text-black-true hocus:text-black hocus:bg-sky",
icon: <ExclamationCircleIcon width={30}/>
icon: <ExclamationCircleIcon width={30} />,
},
error: {
bgColor: "bg-digital-red",
textColor: "text-white",
linkClasses: "transition text-white hocus:text-black hocus:bg-white",
icon: <ExclamationTriangleIcon width={30}/>
icon: <ExclamationTriangleIcon width={30} />,
},
}
const chosenOption = options[configPage.suGlobalMsgType || "success"];
const chosenOption = options[configPage.suGlobalMsgType || "success"]

return (
<div className={"relative z-30 lg:z-0 " + chosenOption.bgColor + " " + chosenOption.textColor}>

<div className="centered flex gap-2xl py-20">
<div className="flex-shrink-0 flex items-center justify-center">
<div className="flex flex-shrink-0 items-center justify-center">
{chosenOption.icon}
{configPage.suGlobalMsgLabel}
</div>

<div>
{configPage.suGlobalMsgHeader &&
<h2 className="text-m3">{configPage.suGlobalMsgHeader}</h2>
}
{configPage.suGlobalMsgHeader && <h2 className="text-m3">{configPage.suGlobalMsgHeader}</h2>}

{configPage.suGlobalMsgMessage?.processed &&
<div className={chosenOption.textColor}>
{formatHtml(configPage.suGlobalMsgMessage.processed.replace(/<a /, `<a class="${chosenOption.linkClasses}" `))}
</div>
}
{configPage.suGlobalMsgMessage?.processed && <div className={chosenOption.textColor}>{formatHtml(configPage.suGlobalMsgMessage.processed.replace(/<a /, `<a class="${chosenOption.linkClasses}" `))}</div>}

{configPage.suGlobalMsgLink?.url &&
<DrupalActionLink href={configPage.suGlobalMsgLink.url} className={chosenOption.linkClasses}>
{configPage.suGlobalMsgLink?.url && (
<DrupalActionLink
href={configPage.suGlobalMsgLink.url}
className={chosenOption.linkClasses}
>
{configPage.suGlobalMsgLink.title}
</DrupalActionLink>
}
)}
</div>
</div>
</div>
)
}

export default GlobalMessage;
export default GlobalMessage
Loading