diff --git a/app/[...slug]/metadata.tsx b/app/[...slug]/metadata.tsx index 7e4c5366..6ff0d821 100644 --- a/app/[...slug]/metadata.tsx +++ b/app/[...slug]/metadata.tsx @@ -1,15 +1,15 @@ -import {DrupalNode, DrupalParagraph} from "next-drupal"; +import {DrupalParagraph} from "next-drupal"; import { BasicPageNodeType, EventNodeType, NewsNodeType, PersonNodeType, - PolicyNodeType, + PolicyNodeType, StanfordNode, WysiwygParagraphType } from "@lib/types"; import {decode} from 'html-entities'; -export const getNodeMetadata = (node: DrupalNode) => { +export const getNodeMetadata = (node: StanfordNode) => { const defaultData = { title: node.title, } diff --git a/app/[...slug]/page.tsx b/app/[...slug]/page.tsx index 08b4e035..1736619a 100644 --- a/app/[...slug]/page.tsx +++ b/app/[...slug]/page.tsx @@ -1,22 +1,22 @@ import {getResourceFromContext} from "@lib/drupal/get-resource"; import {translatePathFromContext} from "@lib/drupal/translate-path"; -import {DrupalNode} from "next-drupal"; import {notFound, redirect} from "next/navigation"; import NodePage from "@components/nodes/pages/node-page"; -import {GetStaticPathsResult, GetStaticPropsContext, Metadata} from "next"; +import {GetStaticPathsResult, Metadata} from "next"; import {DrupalJsonApiParams} from "drupal-jsonapi-params"; import {getPathsFromContext} from "@lib/drupal/get-paths"; import {getNodeMetadata} from "./metadata"; import {getAccessToken} from "@lib/drupal/get-access-token"; import {isDraftMode} from "@lib/drupal/utils"; +import {PageProps, Params, StanfordNode} from "@lib/types"; export const revalidate = 86400; -export const generateMetadata = async (context: GetStaticPropsContext): Promise => { - let node: DrupalNode; +export const generateMetadata = async ({params}: PageProps): Promise => { + let node; try { - node = await getPageData(context); - return getNodeMetadata(node); + node = await getPageData(params); + if (node) return getNodeMetadata(node); } catch (e) { } @@ -29,14 +29,14 @@ class RedirectError extends Error { } } -const getPageData = async (context: GetStaticPropsContext) => { +const getPageData = async(params: Params): Promise => { const draftDev = isDraftMode() const accessToken = draftDev ? await getAccessToken(true) : null; - const path = await translatePathFromContext(context, accessToken ? {accessToken} : {}); + const path = await translatePathFromContext({params}, accessToken ? {accessToken} : {}); // Check for redirect. if (path?.redirect?.[0].to) { - const currentPath = '/' + (typeof context?.params?.slug === 'object' ? context.params.slug.join('/') : context?.params?.slug); + const currentPath = '/' + (typeof params?.slug === 'object' ? params.slug.join('/') : params?.slug); const [destination] = path.redirect; if (destination.to != currentPath) { @@ -48,10 +48,10 @@ const getPageData = async (context: GetStaticPropsContext) => { throw new Error('Unable to translate path') } - if (context?.params?.slug?.[0] === 'node' && path?.entity?.path) { + if (params?.slug?.[0] === 'node' && path?.entity?.path) { throw new RedirectError(path.entity.path); } - return getResourceFromContext(path.jsonapi.resourceName, context, {}, draftDev) + return getResourceFromContext(path.jsonapi.resourceName, {params}, {}, draftDev) } export const generateStaticParams = async () => { @@ -86,15 +86,17 @@ export const generateStaticParams = async () => { return paths.map(path => typeof path !== "string" ? path?.params : path).slice(0, (process.env.BUILD_COMPLETE === 'true' ? -1 : 1)); } -const Page = async (context: GetStaticPropsContext) => { - let node; +const Page = async ({params}: PageProps) => { + let node = null; try { - node = await getPageData(context); + node = await getPageData(params); + } catch (e) { if (e instanceof RedirectError) { redirect(e.message); } } + if (!node) notFound(); return ( diff --git a/app/api/draft/route.tsx b/app/api/draft/route.tsx index bf2e1efd..b6365505 100644 --- a/app/api/draft/route.tsx +++ b/app/api/draft/route.tsx @@ -2,7 +2,7 @@ import {draftMode} from 'next/headers' import {redirect} from 'next/navigation' import {getResourceByPath} from "@lib/drupal/get-resource"; -import {DrupalNode} from "next-drupal"; +import {StanfordNode} from "@lib/types"; export async function GET(request: Request) { // Parse query string parameters @@ -20,7 +20,7 @@ export async function GET(request: Request) { // Fetch the headless CMS to check if the provided `slug` exists // getPostBySlug would implement the required fetching logic to the headless CMS - const node: DrupalNode = await getResourceByPath(slug, {}, true) + const node = await getResourceByPath(slug, {}, true) // If the slug doesn't exist prevent draft mode from being enabled if (!node) { diff --git a/app/error.tsx b/app/error.tsx index 4c8fa899..e5bcdc3c 100644 --- a/app/error.tsx +++ b/app/error.tsx @@ -1,6 +1,6 @@ "use client"; -const Error = ({error, reset}: { error: Error; reset: () => void }) => { +const ErrorPage = ({error, reset}: { error: Error; reset: () => void }) => { console.error(error.message); return (
@@ -11,3 +11,5 @@ const Error = ({error, reset}: { error: Error; reset: () => void }) => {
) } + +export default ErrorPage \ No newline at end of file diff --git a/app/page.tsx b/app/page.tsx index ffb3f713..fa687f05 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -3,12 +3,14 @@ import Rows from "@components/paragraphs/rows/rows"; import {BasicPageNodeType} from "@lib/types"; import Paragraph from "@components/paragraphs/paragraph"; import {isDraftMode} from "@lib/drupal/utils"; +import {notFound} from "next/navigation"; export const revalidate = 86400; const Home = async () => { const draftDev = isDraftMode() const node = await getResourceByPath('/', {}, draftDev); + if(!node) notFound(); return ( <> diff --git a/app/search/page.tsx b/app/search/page.tsx index 726870ec..dc39f1d9 100644 --- a/app/search/page.tsx +++ b/app/search/page.tsx @@ -1,8 +1,8 @@ import {getSearchIndex} from "@lib/drupal/get-search-index"; -import {DrupalNode} from "next-drupal"; import SearchResults from "./search-results"; import {getNodeMetadata} from "../[...slug]/metadata"; import {H1} from "@components/elements/headers"; +import {StanfordNode} from "@lib/types"; export const metadata = { title: "Search", @@ -18,7 +18,7 @@ const Page = () => { const search = async (searchString: string) => { "use server"; - const searchResults: DrupalNode[] = await getSearchIndex('full_site_content', {params: {'filter[fulltext]': searchString}}); + const searchResults: StanfordNode[] = await getSearchIndex('full_site_content', {params: {'filter[fulltext]': searchString}}); return searchResults.map(node => ({ id: node.id, type: node.type, diff --git a/app/search/search-results.tsx b/app/search/search-results.tsx index 9767525f..2ff78b0e 100644 --- a/app/search/search-results.tsx +++ b/app/search/search-results.tsx @@ -1,6 +1,6 @@ "use client"; -import {useEffect, useRef, useState} from "react"; +import {FormEvent, useEffect, useRef, useState} from "react"; import Link from "@components/elements/link"; import {useSearchParams} from "next/navigation"; import {Metadata} from "next"; @@ -16,7 +16,7 @@ interface Result extends Metadata { } const SearchResults = ({search}: { search: (search: string) => Promise }) => { - const inputRef = useRef(null); + const inputRef = useRef(null); const params = useSearchParams(); const [results, setResults] = useState([]) const [searchString, setSearchString] = useState(params.get('q') ?? '') @@ -26,13 +26,14 @@ const SearchResults = ({search}: { search: (search: string) => Promise search(params.get('q') ?? '').then(nodes => setResults(nodes)); }, []) - const onSubmit = (e) => { + const onSubmit = (e: FormEvent) => { e.preventDefault(); setIsLoading(true) - search(inputRef.current.value).then(results => { + const searchString = inputRef.current?.value || ''; + search(searchString).then(results => { setResults(results); - setSearchString(inputRef.current.value); + setSearchString(searchString); setIsLoading(false) }); } diff --git a/next.config.js b/next.config.js index 7ea66c99..d955d279 100644 --- a/next.config.js +++ b/next.config.js @@ -1,4 +1,4 @@ -const drupalUrl = new URL(process.env.NEXT_PUBLIC_DRUPAL_BASE_URL) +const drupalUrl = new URL(process.env.NEXT_PUBLIC_DRUPAL_BASE_URL); const nextConfig = { images: { @@ -10,13 +10,9 @@ const nextConfig = { }, { protocol: drupalUrl.protocol.replace(':', ''), - hostname: drupalUrl.hostname - } - ] - }, - experimental: {}, - typescript: { - ignoreBuildErrors: true, + hostname: drupalUrl.hostname, + }, + ], }, async rewrites() { return { @@ -24,15 +20,15 @@ const nextConfig = { { source: '/_next/image', destination: '/_next/image?url=/no-image.png', - has: [{type: 'query', key: 'url', value: (`${process.env.NEXT_PUBLIC_DRUPAL_BASE_URL}.*`)}], - missing: [{type: 'query', key: 'url', value: '(.*itok=([\\w|-]+))'}] + has: [{ type: 'query', key: 'url', value: (`${process.env.NEXT_PUBLIC_DRUPAL_BASE_URL}.*`) }], + missing: [{ type: 'query', key: 'url', value: '(.*itok=([\\w|-]+))' }], }, { source: '/_next/image', destination: '/_next/image?url=:url', - has: [{type: 'query', key: 'url', value: '(?.*[jpg|png|jpeg|gif]\?itok=([\\w|-]+)).*'}] + has: [{ type: 'query', key: 'url', value: '(?.*[jpg|png|jpeg|gif]\?itok=([\\w|-]+)).*' }], }, - ] + ], }; }, async headers() { @@ -70,8 +66,9 @@ const nextConfig = { ]; }, }; - -const withBundleAnalyzer = require('@next/bundle-analyzer')({ - enabled: process.env.ANALYZE === 'true', -}); -module.exports = withBundleAnalyzer(nextConfig); +if (process.env.NODE_ENV === 'development') { + const withBundleAnalyzer = require('@next/bundle-analyzer')({ + enabled: process.env.ANALYZE === 'true', + }); + module.exports = withBundleAnalyzer(nextConfig); +} \ No newline at end of file diff --git a/package.json b/package.json index 0a753c42..436b4f4a 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "analyze": "ANALYZE=true next build", "preview": "next build && next start", "start": "next start", - "lint": "next lint", + "lint": "next lint && tsc", "storybook": "storybook dev -p 6006", "build-storybook": "storybook build" }, diff --git a/src/components/config-pages/super-footer.tsx b/src/components/config-pages/super-footer.tsx index 6b00f805..6b0f3537 100644 --- a/src/components/config-pages/super-footer.tsx +++ b/src/components/config-pages/super-footer.tsx @@ -4,8 +4,8 @@ import Link from "@components/elements/link"; import {LockClosedIcon} from "@heroicons/react/24/outline"; import {H2} from "@components/elements/headers"; -const SuperFooter = ({configPage}: {configPage?: SuperFooterConfigPageType}) => { - if (!configPage || !configPage.su_super_foot_enabled) { +const SuperFooter = ({configPage}: { configPage: SuperFooterConfigPageType }) => { + if (!configPage.su_super_foot_enabled) { return null; } diff --git a/src/components/elements/icons/FacebookIcon.tsx b/src/components/elements/icons/FacebookIcon.tsx index d06a73f2..26cbff54 100644 --- a/src/components/elements/icons/FacebookIcon.tsx +++ b/src/components/elements/icons/FacebookIcon.tsx @@ -1,4 +1,6 @@ -const FacebookIcon = (props) => ( +import {ComponentProps} from "react"; + +const FacebookIcon = (props: ComponentProps) => ( { +import {ComponentProps} from "react"; + +const InstagramIcon = (props: ComponentProps) => { return ( Instagram icon diff --git a/src/components/elements/icons/LinkedInIcon.tsx b/src/components/elements/icons/LinkedInIcon.tsx index 32fa9f92..cd2ad7df 100644 --- a/src/components/elements/icons/LinkedInIcon.tsx +++ b/src/components/elements/icons/LinkedInIcon.tsx @@ -1,4 +1,6 @@ -const LinkedInIcon = (props) => ( +import {ComponentProps} from "react"; + +const LinkedInIcon = (props: ComponentProps) => ( ( +import {ComponentProps} from "react"; + +const TwitterIcon = (props: ComponentProps) => ( { +import {ComponentProps} from "react"; + +const YoutubeIcon = (props: ComponentProps) => { return ( itemProps?: PropsWithoutRef, @@ -13,7 +14,7 @@ const LoadMoreList = ({buttonText, children, listProps, itemProps, props, itemsP }) => { const [shownItems, setShownItems] = useState(itemsPerPage) const [allowFocus, setAllowFocus] = useState(false); - const ref: RefObject = useRef(null); + const ref: RefObject = useRef(null); const [animationParent] = useAutoAnimate(); const showMoreItems = () => { diff --git a/src/components/elements/wysiwyg.tsx b/src/components/elements/wysiwyg.tsx index c3f389fe..4bf42b71 100644 --- a/src/components/elements/wysiwyg.tsx +++ b/src/components/elements/wysiwyg.tsx @@ -3,7 +3,6 @@ import parse, {HTMLReactParserOptions, Element, domToReact, attributesToProps, D import Image from "next/image"; import Oembed from "@components/elements/ombed"; import React, {PropsWithoutRef} from "react"; -import {ChildNode} from "domhandler"; import {H2, H3, H4, H5, H6} from "@components/elements/headers"; import {twMerge} from "tailwind-merge"; import Script from "next/script"; @@ -38,7 +37,7 @@ const options: HTMLReactParserOptions = { switch (domNode.name) { case "a": return ( - + {domToReact(children, options)} ) diff --git a/src/components/global/page-footer.tsx b/src/components/global/page-footer.tsx index ec226f5e..48ccbe90 100644 --- a/src/components/global/page-footer.tsx +++ b/src/components/global/page-footer.tsx @@ -9,8 +9,8 @@ const PageFooter = async () => { return (
- - + {superFooter && } + {localFooter && }
diff --git a/src/components/nodes/cards/node-card.tsx b/src/components/nodes/cards/node-card.tsx index 894b1a60..6480f025 100644 --- a/src/components/nodes/cards/node-card.tsx +++ b/src/components/nodes/cards/node-card.tsx @@ -1,4 +1,3 @@ -import {DrupalNode} from "next-drupal"; import StanfordCourseCard from "@components/nodes/cards/stanford-course/stanford-course-card"; import StanfordEventCard from "@components/nodes/cards/stanford-event/stanford-event-card"; import StanfordEventSeriesCard from "@components/nodes/cards/stanford-event-series/stanford-event-series-card"; @@ -15,11 +14,11 @@ import { NewsNodeType, PersonNodeType, PolicyNodeType, - PublicationNodeType + PublicationNodeType, StanfordNode } from "@lib/types"; import {JSX} from "react"; -const NodeCard = ({node, headingLevel}: { node: DrupalNode, headingLevel?: string }): JSX.Element | null => { +const NodeCard = ({node, headingLevel}: { node: StanfordNode, headingLevel?: string }): JSX.Element | null => { switch (node.type) { case 'node--stanford_course': return diff --git a/src/components/nodes/cards/stanford-news/stanford-news-card.tsx b/src/components/nodes/cards/stanford-news/stanford-news-card.tsx index d0b6805e..edc5393c 100644 --- a/src/components/nodes/cards/stanford-news/stanford-news-card.tsx +++ b/src/components/nodes/cards/stanford-news/stanford-news-card.tsx @@ -1,7 +1,7 @@ import {NewsNodeType} from "@lib/types"; import Image from "next/image"; import Link from "@components/elements/link"; -import {DrupalTaxonomyTerm} from "next-drupal"; +import {DrupalTaxonomyTerm} from "@lib/types"; import {H2, H3} from "@components/elements/headers"; const StanfordNewsCard = ({node, headingLevel}: { node: NewsNodeType, headingLevel?: string }) => { diff --git a/src/components/nodes/list-item/node-list-item.tsx b/src/components/nodes/list-item/node-list-item.tsx index e8cc1054..41b4f9ea 100644 --- a/src/components/nodes/list-item/node-list-item.tsx +++ b/src/components/nodes/list-item/node-list-item.tsx @@ -1,4 +1,3 @@ -import {DrupalNode} from "next-drupal"; import StanfordCourseListItem from "@components/nodes/list-item/stanford-course/stanford-course-list-item"; import StanfordEventListItem from "@components/nodes/list-item/stanford-event/stanford-event-list-item"; import StanfordEventSeriesListItem @@ -15,10 +14,10 @@ import { EventNodeType, EventSeriesNodeType, NewsNodeType, - PersonNodeType, PolicyNodeType, PublicationNodeType + PersonNodeType, PolicyNodeType, PublicationNodeType, StanfordNode } from "@lib/types"; -const NodeListItem = ({node, headingLevel}: { node: DrupalNode, headingLevel?: string }) => { +const NodeListItem = ({node, headingLevel}: { node: StanfordNode, headingLevel?: string }) => { switch (node.type) { case 'node--stanford_course': return diff --git a/src/components/nodes/list-item/stanford-course/stanford-course-list-item.tsx b/src/components/nodes/list-item/stanford-course/stanford-course-list-item.tsx index 59bef3b9..580b4201 100644 --- a/src/components/nodes/list-item/stanford-course/stanford-course-list-item.tsx +++ b/src/components/nodes/list-item/stanford-course/stanford-course-list-item.tsx @@ -6,7 +6,6 @@ const StanfordCourseListItem = ({node, headingLevel}: { node: CourseNodeType, he const Heading = headingLevel === 'h3' ? H3 : H2; return (
- {node.title} diff --git a/src/components/nodes/list-item/stanford-news/stanford-news-list-item.tsx b/src/components/nodes/list-item/stanford-news/stanford-news-list-item.tsx index 25b3c64b..6017528b 100644 --- a/src/components/nodes/list-item/stanford-news/stanford-news-list-item.tsx +++ b/src/components/nodes/list-item/stanford-news/stanford-news-list-item.tsx @@ -1,7 +1,7 @@ import {NewsNodeType} from "@lib/types"; import Image from "next/image"; import Link from "@components/elements/link"; -import {DrupalTaxonomyTerm} from "next-drupal"; +import {DrupalTaxonomyTerm} from "@lib/types"; import {H2, H3} from "@components/elements/headers"; const StanfordNewsListItem = ({node, headingLevel}: { node: NewsNodeType, headingLevel?: string }) => { diff --git a/src/components/nodes/pages/node-page.tsx b/src/components/nodes/pages/node-page.tsx index 77087687..59c0a71c 100644 --- a/src/components/nodes/pages/node-page.tsx +++ b/src/components/nodes/pages/node-page.tsx @@ -1,4 +1,3 @@ -import {DrupalNode} from "next-drupal"; import StanfordPagePage from "@components/nodes/pages/stanford-page/stanford-page-page"; import StanfordPersonPage from "@components/nodes/pages/stanford-person/stanford-person-page"; import StanfordEventPage from "@components/nodes/pages/stanford-event/stanford-event-page"; @@ -13,10 +12,10 @@ import { EventNodeType, EventSeriesNodeType, NewsNodeType, - PersonNodeType, PolicyNodeType, PublicationNodeType + PersonNodeType, PolicyNodeType, PublicationNodeType, StanfordNode } from "@lib/types"; -const NodePage = ({node}: { node: DrupalNode }) => { +const NodePage = ({node}: { node: StanfordNode }) => { return ( <> {!node.status && @@ -31,7 +30,7 @@ const NodePage = ({node}: { node: DrupalNode }) => { ) } -const Node = ({node}: { node: DrupalNode }) => { +const Node = ({node}: { node: StanfordNode }) => { switch (node.type) { case 'node--stanford_course': return diff --git a/src/components/nodes/pages/stanford-news/stanford-news-page.tsx b/src/components/nodes/pages/stanford-news/stanford-news-page.tsx index 9e4efc7f..5720be1c 100644 --- a/src/components/nodes/pages/stanford-news/stanford-news-page.tsx +++ b/src/components/nodes/pages/stanford-news/stanford-news-page.tsx @@ -3,7 +3,7 @@ import {redirect} from "next/navigation"; import Image from "next/image"; import Rows from "@components/paragraphs/rows/rows"; import SocialIcons from "@components/nodes/pages/stanford-news/social-icons"; -import {DrupalTaxonomyTerm} from "next-drupal"; +import {DrupalTaxonomyTerm} from "@lib/types"; import {H1} from "@components/elements/headers"; const StanfordNewsPage = ({node}: { node: NewsNodeType }) => { diff --git a/src/components/nodes/pages/stanford-page/stanford-page-page.tsx b/src/components/nodes/pages/stanford-page/stanford-page-page.tsx index fd38ed55..8b009e62 100644 --- a/src/components/nodes/pages/stanford-page/stanford-page-page.tsx +++ b/src/components/nodes/pages/stanford-page/stanford-page-page.tsx @@ -5,6 +5,7 @@ import Paragraph from "@components/paragraphs/paragraph"; import {H1} from "@components/elements/headers"; const StanfordPagePage = ({node}: { node: BasicPageNodeType }) => { + console.log(node); const fullWidth = node.layout_selection?.resourceIdObjMeta.drupal_internal__target_id === 'stanford_basic_page_full'; return (
diff --git a/src/components/paragraphs/stanford-entity/entity-paragraph.tsx b/src/components/paragraphs/stanford-entity/entity-paragraph.tsx index 5fd70cc6..bffad245 100644 --- a/src/components/paragraphs/stanford-entity/entity-paragraph.tsx +++ b/src/components/paragraphs/stanford-entity/entity-paragraph.tsx @@ -1,13 +1,12 @@ -import {EntityTeaserParagraphType} from "@lib/types"; +import {EntityTeaserParagraphType, StanfordNode} from "@lib/types"; import {getResources} from "@lib/drupal/get-resource"; import Wysiwyg from "@components/elements/wysiwyg"; import NodeCard from "@components/nodes/cards/node-card"; import Button from "@components/elements/button"; -import {DrupalNode} from "next-drupal"; import {H2} from "@components/elements/headers"; const EntityParagraph = async ({paragraph}: { paragraph: EntityTeaserParagraphType }) => { - const items = await getResources(paragraph.su_entity_item ?? []); + const items = await getResources(paragraph.su_entity_item ?? []); const entities = items.filter(item => item); const gridCols = [ 'lg:grid-cols-3', diff --git a/src/components/views/card-view-grid.tsx b/src/components/views/card-view-grid.tsx index 6cff706a..f473978f 100644 --- a/src/components/views/card-view-grid.tsx +++ b/src/components/views/card-view-grid.tsx @@ -1,8 +1,8 @@ -import {DrupalNode} from "next-drupal"; import NodeCard from "@components/nodes/cards/node-card"; import LoadMoreList from "@components/elements/load-more-list"; +import {StanfordNode} from "@lib/types"; -const CardViewGrid = ({items, headingLevel}: { items: DrupalNode[], headingLevel: string }) => { +const CardViewGrid = ({items, headingLevel}: { items: StanfordNode[], headingLevel: string }) => { return ( { args = args ? args + '/0/0/0' : '0/0/0'; - const items = await getViewItems(view, itemsToDisplay, args.split('/')); + const items = await getViewItems(view, itemsToDisplay, args.split('/')); if (items.length === 0) { return emptyMessage ?
{emptyMessage}
: null; diff --git a/src/components/views/stanford-events/events-filtered-list-view.tsx b/src/components/views/stanford-events/events-filtered-list-view.tsx index b30fa4fa..3bcb93a1 100644 --- a/src/components/views/stanford-events/events-filtered-list-view.tsx +++ b/src/components/views/stanford-events/events-filtered-list-view.tsx @@ -7,16 +7,17 @@ import {useMemo, useState} from "react"; import LoadMoreList from "@components/elements/load-more-list"; import StanfordEventListItem from "@components/nodes/list-item/stanford-event/stanford-event-list-item"; import {EventNodeType} from "@lib/types"; -import {DrupalTaxonomyTerm} from "next-drupal"; +import {DrupalTaxonomyTerm} from "@lib/types"; +import {SelectOptionDefinition, SelectValue} from "@mui/base/useSelect"; -const getTopicOptions = (eventItems: EventNodeType[] = [], topicTree: DrupalTaxonomyTerm[] = []) => { - const topicOptions = []; +const getTopicOptions = (eventItems: EventNodeType[] = [], topicTree: DrupalTaxonomyTerm[] = []): SelectOptionDefinition[] => { + const topicOptions: SelectOptionDefinition[] = []; - const cleanTopic = (topic) => { + const cleanTopic = (topic: DrupalTaxonomyTerm): boolean => { if (topic.below) { topic.below = topic.below.filter(childTopic => cleanTopic(childTopic)); } - if (topic.below?.length > 0) return true; + if (!topic.below?.length) return true; return !!eventItems.find(event => { return event.su_event_type?.map(eventTerm => eventTerm.id).includes(topic.id); @@ -31,8 +32,8 @@ const getTopicOptions = (eventItems: EventNodeType[] = [], topicTree: DrupalTaxo } const EventsFilteredListView = ({items, topics}: { items: EventNodeType[], topics: DrupalTaxonomyTerm[] }) => { - const [chosenTopic, setChosenTopic] = useState(''); - const [displayedEvents, setDisplayedEvents] = useState(items); + const [chosenTopic, setChosenTopic] = useState(''); + const [displayedEvents, setDisplayedEvents] = useState(items); const topicTree = useMemo(() => getTaxonomyTree(topics), [topics]); const topicOptions = useMemo(() => getTopicOptions(items, topicTree), [topics]); @@ -43,16 +44,22 @@ const EventsFilteredListView = ({items, topics}: { items: EventNodeType[], topic setDisplayedEvents(items); return; } - const topicIds = []; - const getTopicIds = (topicTerm) => { + const topicIds: string[] = []; + + const buildChosenTopicIds = (topicTerm: DrupalTaxonomyTerm | undefined): void => { + if (!topicTerm) return; + topicIds.push(topicTerm.id); - topicTerm.below?.map(belowTerm => getTopicIds(belowTerm)); + topicTerm.below?.map(belowTerm => buildChosenTopicIds(belowTerm)); } - getTopicIds(topicTree.find(term => term.id === chosenTopic)); + + buildChosenTopicIds(topicTree.find(term => term.id === chosenTopic)); + const matchingEvents = items.filter(event => { const eventTopics = event.su_event_type?.map(eventTerm => eventTerm.id) return topicIds.filter(value => eventTopics?.includes(value)).length > 0; }); + setDisplayedEvents(matchingEvents); } @@ -63,12 +70,12 @@ const EventsFilteredListView = ({items, topics}: { items: EventNodeType[], topic setChosenTopic(value)} + onChange={(e, value: SelectValue) => setChosenTopic(value as string || '')} />
-
Showing {displayedEvents.length} of {items.length} events.
+
Showing {displayedEvents.length} of {items.length} events.
event.id).join(',')} buttonText={<>Load More Events} diff --git a/src/components/views/stanford-events/events-list-view.tsx b/src/components/views/stanford-events/events-list-view.tsx index 7df10e87..0b12c8d0 100644 --- a/src/components/views/stanford-events/events-list-view.tsx +++ b/src/components/views/stanford-events/events-list-view.tsx @@ -4,7 +4,7 @@ import {getViewItems} from "@components/views/view"; import LoadMoreList from "@components/elements/load-more-list"; import EventsFilteredListView from "@components/views/stanford-events/events-filtered-list-view"; import {getResourceCollection} from "@lib/drupal/get-resource"; -import {DrupalTaxonomyTerm} from "next-drupal"; +import {DrupalTaxonomyTerm} from "@lib/types"; import {trimNodeData} from "@lib/drupal/utils"; interface Props { diff --git a/src/components/views/stanford-page/page-list-view.tsx b/src/components/views/stanford-page/page-list-view.tsx index c4059e04..e174be63 100644 --- a/src/components/views/stanford-page/page-list-view.tsx +++ b/src/components/views/stanford-page/page-list-view.tsx @@ -1,7 +1,7 @@ import StanfordPageListItem from "@components/nodes/list-item/stanford-page/stanford-page-list-item"; import {getViewItems} from "@components/views/view"; -import {DrupalNode} from "next-drupal"; import LoadMoreList from "@components/elements/load-more-list"; +import {BasicPageNodeType} from "@lib/types"; interface Props { view: string @@ -14,7 +14,7 @@ interface Props { const PageListView = async ({view, args, itemsToDisplay, emptyMessage, headingLevel}: Props) => { args = args ? args + '/0/0/0' : '0/0/0/0'; - const items = await getViewItems(view, itemsToDisplay, args.split('/')); + const items = await getViewItems(view, itemsToDisplay, args.split('/')); if (items.length === 0) { return emptyMessage ?
{emptyMessage}
: null; } diff --git a/src/components/views/stanford-publications/publications-apa-view.tsx b/src/components/views/stanford-publications/publications-apa-view.tsx index 61d0f499..778363d0 100644 --- a/src/components/views/stanford-publications/publications-apa-view.tsx +++ b/src/components/views/stanford-publications/publications-apa-view.tsx @@ -1,6 +1,4 @@ import {PublicationNodeType} from "@lib/types"; -import StanfordPublicationListItem - from "@components/nodes/list-item/stanford-publication/stanford-publication-list-item"; import {getViewItems} from "@components/views/view"; import CardViewGrid from "@components/views/card-view-grid"; diff --git a/src/components/views/stanford-publications/publications-chicago-view.tsx b/src/components/views/stanford-publications/publications-chicago-view.tsx index 200454ad..a312c771 100644 --- a/src/components/views/stanford-publications/publications-chicago-view.tsx +++ b/src/components/views/stanford-publications/publications-chicago-view.tsx @@ -1,7 +1,8 @@ import {PublicationNodeType} from "@lib/types"; -import StanfordCourseListItem from "@components/nodes/list-item/stanford-course/stanford-course-list-item"; import {getViewItems} from "@components/views/view"; import LoadMoreList from "@components/elements/load-more-list"; +import StanfordPublicationListItem + from "@components/nodes/list-item/stanford-publication/stanford-publication-list-item"; interface Props { view: string @@ -24,7 +25,7 @@ const PublicationsChicagoView = async ({view, args, itemsToDisplay, emptyMessage itemProps={{className: "border-b border-black-20 last-of-type:border-0 pb-10 last:pb-0 pt-10 first:pt-0"}} > {items.map(item => - + )}
) diff --git a/src/components/views/view.tsx b/src/components/views/view.tsx index fbd8932f..5bfe1b3c 100644 --- a/src/components/views/view.tsx +++ b/src/components/views/view.tsx @@ -11,10 +11,10 @@ import CourseCardView from "@components/views/stanford-courses/course-card-view" import PublicationsApaView from "@components/views/stanford-publications/publications-apa-view"; import PublicationsChicagoView from "@components/views/stanford-publications/publications-chicago-view"; import {DrupalJsonApiParams} from "drupal-jsonapi-params"; -import {DrupalNode} from "next-drupal"; import {getView} from "@lib/drupal/get-view"; import {getResources} from "@lib/drupal/get-resource"; import {JSX} from "react"; +import {StanfordNode} from "@lib/types"; interface Props { viewId: string; @@ -87,13 +87,15 @@ export const getViewItems = async (view: string, itemsToDisplay: number = - // Find new way to add the item limit since this throws errors. drupalParams.addPageLimit(itemsToDisplay); } - let items: DrupalNode[] = []; + let items: StanfordNode[] = []; try { - const viewData = await getView(view, {params: drupalParams.getQueryObject()}); + const viewData = await getView(view, {params: drupalParams.getQueryObject()}); items = viewData.results ?? []; - } catch (e) { - console.log(`Unable to fetch view ${view}: ${e.message}`) + } catch (e: unknown) { + if (e instanceof Error) { + console.log(`Unable to fetch view ${view}: ${e.message}`) + } } return getResources(items); } diff --git a/src/lib/drupal/get-resource.tsx b/src/lib/drupal/get-resource.tsx index 5acd9f5f..df63bd97 100644 --- a/src/lib/drupal/get-resource.tsx +++ b/src/lib/drupal/get-resource.tsx @@ -7,8 +7,9 @@ import {buildUrl, buildHeaders, getJsonApiPathForResourceType, getPathFromContex import {deserialize} from "@lib/drupal/deserialize"; import {GetStaticPropsContext} from "next"; import {JsonApiParams} from "next-drupal"; +import {PageProps, StanfordConfigPage} from "@lib/types"; -export async function getResources(items: { type: string, id: string }[], draftDev:boolean = false): Promise { +export async function getResources(items: { type: string, id: string }[], draftDev: boolean = false): Promise { const requests: PromiseLike[] = []; items.map(item => requests.push(getResource(item.type, item.id, {}, draftDev))); // @ts-ignore @@ -20,7 +21,7 @@ export async function getResources(items: { type: string, id: string }[], dra export async function getResourceFromContext( type: string, - context: GetStaticPropsContext, + context: PageProps, options?: { prefix?: string deserialize?: boolean @@ -29,7 +30,7 @@ export async function getResourceFromContext( isVersionable?: boolean }, draftMode: boolean = false -): Promise { +): Promise { options = { deserialize: true, // Add support for revisions for node by default. @@ -40,15 +41,10 @@ export async function getResourceFromContext( const path = getPathFromContext(context, options?.prefix) - const previewData = context.previewData as { resourceVersion?: string } - const resource = await getResourceByPath(path, { deserialize: options.deserialize, isVersionable: options.isVersionable, - locale: context.locale, - defaultLocale: context.defaultLocale, params: { - resourceVersion: previewData?.resourceVersion, ...options?.params, }, }, draftMode) @@ -64,7 +60,7 @@ export async function getResourceByPath( isVersionable?: boolean } & JsonApiWithLocaleOptions, draftMode: boolean = false -): Promise { +): Promise { options = { deserialize: true, isVersionable: false, @@ -73,28 +69,11 @@ export async function getResourceByPath( } if (!path) { - return null - } - - if ( - options.locale && - options.defaultLocale && - path.indexOf(options.locale) !== 1 - ) { - path = path === "/" ? path : path.replace(/^\/+/, "") - path = getPathFromContext({ - params: {slug: [path]}, - locale: options.locale, - defaultLocale: options.defaultLocale, - }) + return } const {resourceVersion = "rel:latest-version", ...params} = options.params - if (options.isVersionable) { - params.resourceVersion = resourceVersion - } - const resourceParams = stringify(params) const payload = [ @@ -112,21 +91,7 @@ export async function getResourceByPath( }, ] - // Localized subrequests. - // I was hoping we would not need this but it seems like subrequests is not properly - // setting the jsonapi locale from a translated path. - let subrequestsPath = "/subrequests" - if ( - options.locale && - options.defaultLocale && - options.locale !== options.defaultLocale - ) { - subrequestsPath = `/${options.locale}/subrequests` - } - - const url = buildUrl(subrequestsPath, { - _format: "json", - }) + const url = buildUrl("/subrequests", {_format: "json"}) const response = await fetch(url.toString(), { method: "POST", @@ -140,7 +105,7 @@ export async function getResourceByPath( const json = await response.json() if (!json["resolvedResource#uri{0}"]) { - return null + return } const data = JSON.parse(json["resolvedResource#uri{0}"]?.body) @@ -165,10 +130,7 @@ export async function getResourceCollection( ...options, } - const apiPath = await getJsonApiPathForResourceType( - type, - options?.locale !== options?.defaultLocale ? options.locale : undefined - ) + const apiPath = await getJsonApiPathForResourceType(type) if (!apiPath) { throw new Error(`Error: resource of type ${type} not found.`) @@ -206,10 +168,7 @@ export async function getResource( ...options, } - const apiPath = await getJsonApiPathForResourceType( - type, - options?.locale !== options?.defaultLocale ? options.locale : undefined - ) + const apiPath = await getJsonApiPathForResourceType(type) if (!apiPath) { throw new Error(`Error: resource of type ${type} not found.`) @@ -231,20 +190,20 @@ export async function getResource( return options.deserialize ? deserialize(json) : json } -export async function getConfigPageResource( +export async function getConfigPageResource( name: string, options?: { deserialize?: boolean accessToken?: AccessToken - } & JsonApiWithLocaleOptions): Promise { + } & JsonApiWithLocaleOptions): Promise { let response; try { response = await getResourceCollection(`config_pages--${name}`, options); if (response.length === 0) { - return null; + return; } } catch (e) { - return null; + return; } return response.at(0); diff --git a/src/lib/drupal/get-taxonomy-tree.tsx b/src/lib/drupal/get-taxonomy-tree.tsx index a42a43fa..bd09de00 100644 --- a/src/lib/drupal/get-taxonomy-tree.tsx +++ b/src/lib/drupal/get-taxonomy-tree.tsx @@ -1,14 +1,16 @@ -import {DrupalTaxonomyTerm} from "next-drupal"; +import {DrupalTaxonomyTerm} from "@lib/types"; -export const getTaxonomyTree = (terms: DrupalTaxonmYTerm[]) => { +export const getTaxonomyTree = (terms: DrupalTaxonomyTerm[]): DrupalTaxonomyTerm[] => { const {below} = buildTaxonomyTree(terms); - return below; + return below || terms; } + export function buildTaxonomyTree( terms: DrupalTaxonomyTerm[], parent: DrupalTaxonomyTerm["id"] = "virtual" -) { +): { below?: DrupalTaxonomyTerm[] } { + if (!terms?.length) { return { below: [], diff --git a/src/lib/drupal/get-view.tsx b/src/lib/drupal/get-view.tsx index 4cb30267..836e55f1 100644 --- a/src/lib/drupal/get-view.tsx +++ b/src/lib/drupal/get-view.tsx @@ -24,15 +24,10 @@ export async function getView( ...options, } - const localePrefix = - options?.locale && options.locale !== options.defaultLocale - ? `/${options.locale}` - : "" - const [viewId, displayId] = name.split("--") const url = buildUrl( - `${localePrefix}/jsonapi/views/${viewId}/${displayId}`, + `/jsonapi/views/${viewId}/${displayId}`, options.params ) diff --git a/src/lib/drupal/translate-path.tsx b/src/lib/drupal/translate-path.tsx index 89e2dfc3..0d661203 100644 --- a/src/lib/drupal/translate-path.tsx +++ b/src/lib/drupal/translate-path.tsx @@ -1,6 +1,6 @@ -import {GetStaticPropsContext} from "next"; import {AccessToken, DrupalTranslatedPath} from "next-drupal"; import {buildHeaders, buildUrl, getPathFromContext} from "./utils"; +import {PageProps} from "@lib/types"; export async function translatePath( path: string, @@ -26,7 +26,7 @@ export async function translatePath( } export async function translatePathFromContext( - context: GetStaticPropsContext, + context: PageProps, options?: { accessToken?: AccessToken prefix?: string diff --git a/src/lib/drupal/utils.tsx b/src/lib/drupal/utils.tsx index 49e7286a..a1157072 100644 --- a/src/lib/drupal/utils.tsx +++ b/src/lib/drupal/utils.tsx @@ -1,11 +1,10 @@ // @ts-nocheck import {stringify} from "qs" -import {GetStaticPropsContext} from "next"; import {AccessToken, Locale} from "next-drupal/src/types"; import {getAccessToken} from "./get-access-token"; -import {DrupalNode} from "next-drupal"; import {draftMode} from "next/headers"; +import {PageProps, StanfordNode} from "@lib/types"; const JSONAPI_PREFIX = process.env.DRUPAL_JSONAPI_PREFIX || "/jsonapi" @@ -35,20 +34,15 @@ export const buildUrl = ( } export function getPathFromContext( - context: GetStaticPropsContext, + context: PageProps, prefix = "" -) { +): string { let { slug } = context.params slug = Array.isArray(slug) ? slug.map((s) => encodeURIComponent(s)).join("/") : slug - // Handle locale. - if (context.locale && context.locale !== context.defaultLocale) { - slug = `/${context.locale}/${slug}` - } - return !slug ? process.env.DRUPAL_FRONT_PAGE : prefix @@ -123,7 +117,7 @@ export async function getJsonApiIndex( return await response.json() } -export const trimNodeData = (node: DrupalNode | DrupalNode[], desiredProperties: string[]): T => { +export const trimNodeData = (node: StanfordNode | StanfordNode[], desiredProperties: string[]): T => { if (!Array.isArray(node)) { const data = {id: node.id, title: node.title, path: node.path}; desiredProperties.map(property => data[property] = node[property]); diff --git a/src/lib/types.tsx b/src/lib/types.tsx index 623bae22..dac163fe 100644 --- a/src/lib/types.tsx +++ b/src/lib/types.tsx @@ -1,8 +1,38 @@ -import {DrupalFile, DrupalMedia, DrupalNode, DrupalParagraph, DrupalTaxonomyTerm} from "next-drupal"; +import { + DrupalFile, + DrupalMedia, + DrupalNode, + DrupalParagraph, + DrupalTaxonomyTerm as NextDrupalTaxonomyTerm +} from "next-drupal"; import {JsonApiResource} from "next-drupal/src/types"; +export interface Params { + slug: string | string[] +} + +export interface PageProps { + params: Params + searchParams?: { [key: string]: string | string[] | undefined } +} + +export interface DrupalTaxonomyTerm extends NextDrupalTaxonomyTerm { + parent: [{ id: string }] + below?: DrupalTaxonomyTerm[] +} + +export type StanfordNode = + BasicPageNodeType + | CourseNodeType + | PersonNodeType + | EventNodeType + | EventSeriesNodeType + | PublicationNodeType + | NewsNodeType; + // Node Types. export interface BasicPageNodeType extends DrupalNode { + type: "node--stanford_page" su_basic_page_type?: DrupalTaxonomyTerm[] su_page_banner?: BannerParagraphType su_page_components?: DrupalParagraph[] @@ -13,6 +43,7 @@ export interface BasicPageNodeType extends DrupalNode { } export interface CourseNodeType extends DrupalNode { + type: "node--stanford_course" body?: string su_course_academic_year?: string su_course_code?: string @@ -27,6 +58,7 @@ export interface CourseNodeType extends DrupalNode { } export interface EventNodeType extends DrupalNode { + type: "node--stanford_event" body?: string su_event_alt_loc?: string su_event_audience?: DrupalTaxonomyTerm[] @@ -50,6 +82,7 @@ export interface EventNodeType extends DrupalNode { } export interface EventSeriesNodeType extends DrupalNode { + type: "node--stanford_event_series" su_event_series_components: DrupalParagraph[] su_event_series_dek: string su_event_series_event: DrupalNode[] @@ -60,6 +93,7 @@ export interface EventSeriesNodeType extends DrupalNode { } export interface NewsNodeType extends DrupalNode { + type: "node--stanford_news" su_news_banner?: DrupalImageMediaType | DrupalVideoMediaType su_news_banner_media_caption?: string su_news_byline?: string @@ -74,6 +108,7 @@ export interface NewsNodeType extends DrupalNode { } export interface PersonNodeType extends DrupalNode { + type: "node--stanford_person" body?: string su_person_academic_appt?: string su_person_address?: string @@ -279,6 +314,12 @@ export interface DrupalEmbeddableMediaType extends DrupalMedia { } // Config Pages +export type StanfordConfigPage = GlobalMessageConfigPageType + | SiteSettingsConfigPageType + | LockupSettingsConfigPageType + | LocalFooterConfigPageType + | SuperFooterConfigPageType + export interface GlobalMessageConfigPageType extends JsonApiResource { su_global_msg_type: 'error' | 'plain' | 'warning' | 'info' | 'success' su_global_msg_enabled: boolean diff --git a/src/styles/typography/global-message.tsx b/src/styles/typography/global-message.tsx index 2071df1e..5dc5fc1b 100644 --- a/src/styles/typography/global-message.tsx +++ b/src/styles/typography/global-message.tsx @@ -1,3 +1,4 @@ +// @ts-nocheck module.exports = function () { return function ({addUtilities, theme}) { const components = { diff --git a/src/styles/typography/local-footer.tsx b/src/styles/typography/local-footer.tsx index a106087c..2d2794fa 100644 --- a/src/styles/typography/local-footer.tsx +++ b/src/styles/typography/local-footer.tsx @@ -1,3 +1,4 @@ +// @ts-nocheck module.exports = function () { return function ({addComponents, theme}) { const components = { diff --git a/src/styles/typography/wysiwyg.tsx b/src/styles/typography/wysiwyg.tsx index f5c506cf..805af649 100644 --- a/src/styles/typography/wysiwyg.tsx +++ b/src/styles/typography/wysiwyg.tsx @@ -1,3 +1,4 @@ +// @ts-nocheck module.exports = function () { return function ({addComponents, theme}) { diff --git a/tsconfig.json b/tsconfig.json index 0d004e1d..bd5521f2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,16 +14,19 @@ "isolatedModules": true, "jsx": "preserve", "incremental": true, - "plugins": [ - { - "name": "next" - } - ], + "plugins": [{"name": "next"}], "paths": { "@components/*": ["./src/components/*"], "@lib/*": ["./src/lib/*"] } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "include": [ + "next-env.d.ts", + "src/**/*.ts", + "src/**/*.tsx", + "app/**/*.ts", + "app/**/*.tsx", + ".next/types/**/*.ts" + ], "exclude": ["node_modules"] }